{ ******************************************************************
  sncCurrency version 1.1
  Copyright (C) 1999 by Moore Xu.  All Rights Reserved.
  e-mail: moorexu@163.net
  Description: very cool component set for chinese currency input.

  LICENCE CONDITIONS

  USE OF THE ENCLOSED SOFTWARE INDICATES YOUR ASSENT TO THE FOLLOWING
  LICENCE CONDITIONS:
  1. You are free to use sncCurrency in compiled form for any purpose.
  However, use in commercial or shareware applications requires registration.
  2. The sncCurrency source code or DCU, in whole or in part, modified or
  unmodified, may not be redistributed for profit or as part of another
  commercial or shareware software package without express written permission
  from me.
  3. Software using this code must contain a visible line of credit.
  This code is distributed "as is" without any warranties, express or implied.
  4. Register fee: $10 for person and $100 for organization.

  Created:     1999.06.01
  Last change: 1999.08.06
  History:
  1.0:  Initial release
  1.1:  Fixed bug and rewrite whole component in standard code
 ****************************************************************** }

unit sncCurrency;

{$R-}

interface

uses Messages, SysUtils, Classes, Controls, Forms, Graphics,
     Windows, StdCtrls, Extctrls;

const
  FloatMaxLength = 18;
  BorderStyles: array[TBorderStyle] of DWORD = (0, WS_BORDER);

type
  TsncCurrencyLabel = class(TGraphicControl)
  private
    FBorderStyle: TBorderStyle;
    FCellWidth: Integer;
    FCtl3D: Boolean;
    FDecimalNumber: Integer;
    FDecimalSeparatorColor: TColor;
    FDecimalSymbols: TStrings;
    FDigitalNumber: Integer;
    FDigitalSymbols: TStrings;
    FGridLineColor: TColor;
    FGridLineWidth: Integer;
    FKilobitSeparatorColor: TColor;
    FTextLayout: TTextLayout;
    BorderSize: Integer;
    IntCellOffset: Integer;
    IntCellWidth: Integer;

    procedure AutoInitialize;
    procedure AutoDestroy;
    function GetBorderStyle: TBorderStyle;
    procedure SetBorderStyle(Value: TBorderStyle);
    function GetCellWidth: Integer;
    procedure SetCellWidth(Value: Integer);
    function GetCtl3D: Boolean;
    procedure SetCtl3D(Value: Boolean);
    function GetDecimalNumber: Integer;
    procedure SetDecimalNumber(Value: Integer);
    function GetDecimalSeparatorColor: TColor;
    procedure SetDecimalSeparatorColor(Value: TColor);
    procedure SetDecimalSymbols(Value: TStrings);
    function GetDigitalNumber: Integer;
    procedure SetDigitalNumber(Value: Integer);
    procedure SetDigitalSymbols(Value: TStrings);
    function GetGridLineColor: TColor;
    procedure SetGridLineColor(Value: TColor);
    function GetGridLineWidth: Integer;
    procedure SetGridLineWidth(Value: Integer);
    function GetKilobitSeparatorColor: TColor;
    procedure SetKilobitSeparatorColor(Value: TColor);
    function GetTextLayout: TTextLayout;
    procedure SetTextLayout(Value: TTextLayout);
    procedure DrawGrid;
    procedure DrawText;
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property BorderStyle: TBorderStyle read GetBorderStyle write SetBorderStyle
             default bsSingle;
    property CellWidth: Integer read GetCellWidth write SetCellWidth
             default -1;
    property Ctl3D: Boolean read GetCtl3D write SetCtl3D
             default True;
    property DecimalNumber: Integer read GetDecimalNumber write SetDecimalNumber
             default 2;
    property DecimalSeparatorColor: TColor read GetDecimalSeparatorColor write SetDecimalSeparatorColor
             default clRed;
    property DecimalSymbols: TStrings read FDecimalSymbols write SetDecimalSymbols;
    property DigitalNumber: Integer read GetDigitalNumber write SetDigitalNumber
             default 10;
    property DigitalSymbols: TStrings read FDigitalSymbols write SetDigitalSymbols;
    property GridLineColor: TColor read GetGridLineColor write SetGridLineColor
             default clSilver;
    property GridLineWidth: Integer read GetGridLineWidth write SetGridLineWidth
             default 1;
    property KilobitSeparatorColor: TColor read GetKilobitSeparatorColor write SetKilobitSeparatorColor
             default clBlack;
    property TextLayout: TTextLayout read GetTextLayout write SetTextLayout
             default tlCenter;

    property OnClick;
    property OnDblClick;
    property OnDragDrop;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property Color;
    property Font;
    property ParentColor;
    property ParentFont;
    property ParentShowHint;
    property ShowHint;
    property Visible;
  end;

  TsncCustomCurrencyEdit = class(TCustomControl)
  private
    FBorderStyle: TBorderStyle;
    FCellWidth: Integer;
    FCurrencySymbol: String;
    FCurrencySymbolAligned: Boolean;
    FDecimalNumber: Integer;
    FDecimalSeparatorColor: TColor;
    FDigitalNumber: Integer;
    FFocusedColor: TColor;
    FGridLineColor: TColor;
    FGridLineWidth: Integer;
    FKilobitSeparatorColor: TColor;
    FMaxLength: Integer;
    FMoveOutAllowed: Boolean;
    FNegativeColor: TColor;
    FNegativeFont: TFont;
    FReadOnly: Boolean;
    FShowNegativeColor: Boolean;
    FShowNegativeFont: Boolean;
    FShowNegativeSign: Boolean;
    FTextLayout: TTextLayout;
    FValue: Extended;
    FZeroEmpty: Boolean;
    FOnChange: TNotifyEvent;
    FModified: Boolean;
    FOnBeforeChange: TNotifyEvent;
    FOnMoveOut: TKeyEvent;

    FCursorTimer: TTimer;
    FCursorVisible: Boolean;
    FCursorWidth: Integer;
    FCursorXPos: Integer;
    FCursorY: Integer;
    FDotLength: Integer;
    FFormatString: String;
    FWorkCellOffset: Integer;
    FWorkCellWidth: Integer;
    FNegativeSign: Integer;
    FOriginValue: Extended;

    procedure AutoInitialize;
    procedure AutoDestroy;
    function GetBorderStyle: TBorderStyle;
    procedure SetBorderStyle(Value: TBorderStyle);
    function GetCellWidth: Integer;
    procedure SetCellWidth(Value: Integer);
    function GetCurrencySymbol: String;
    procedure SetCurrencySymbol(Value: String);
    function GetCurrencySymbolAligned: Boolean;
    procedure SetCurrencySymbolAligned(Value: Boolean);
    function GetDecimalNumber: Integer;
    procedure SetDecimalNumber(Value: Integer);
    function GetDecimalSeparatorColor: TColor;
    procedure SetDecimalSeparatorColor(Value: TColor);
    function GetDigitalNumber: Integer;
    procedure SetDigitalNumber(Value: Integer);
    function GetFocusedColor: TColor;
    procedure SetFocusedColor(Value: TColor);
    function GetGridLineColor: TColor;
    procedure SetGridLineColor(Value: TColor);
    function GetGridLineWidth: Integer;
    procedure SetGridLineWidth(Value: Integer);
    function GetKilobitSeparatorColor: TColor;
    procedure SetKilobitSeparatorColor(Value: TColor);
    function GetMaxLength: Integer;
    procedure SetMaxLength(Value: Integer);
    function GetMoveOutAllowed: Boolean;
    procedure SetMoveOutAllowed(Value: Boolean);
    function GetNegativeColor: TColor;
    procedure SetNegativeColor(Value: TColor);
    procedure SetNegativeFont(Value: TFont);
    function GetReadOnly: Boolean;
    procedure SetReadOnly(Value: Boolean);
    function GetShowNegativeColor: Boolean;
    procedure SetShowNegativeColor(Value: Boolean);
    function GetShowNegativeFont: Boolean;
    procedure SetShowNegativeFont(Value: Boolean);
    function GetShowNegativeSign: Boolean;
    procedure SetShowNegativeSign(Value: Boolean);
    function GetTextLayout: TTextLayout;
    procedure SetTextLayout(Value: TTextLayout);
    function GetValue: Extended;
    procedure SetValue(Value: Extended);
    function GetZeroEmpty: Boolean;
    procedure SetZeroEmpty(Value: Boolean);
    procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED;
    procedure CMEnter(var Message: TMessage); message CM_ENTER;
    procedure SyncCursorPos;
    procedure ChangeValue(Value: Extended);
    procedure CursorTimerHandle(Sender: TObject);
    procedure DecodeCursorX(X: Integer);
    procedure DrawCursor;
    procedure DrawGrid;
    procedure DrawText;
    procedure InitCursorX(X: Integer);
    procedure SetCursorState(Visible: Boolean);
    procedure WMChar(var Message: TMessage); message WM_CHAR;
    procedure WMKeyDown(var Message: TMessage); message WM_KEYDOWN;
    procedure CNKEYDOWN(var Message: TMessage); message CN_KEYDOWN;
    procedure WMKillFocus(var Message: TMessage); message WM_KILLFOCUS;
    procedure WMLButtonDown(var Message: TMessage); message WM_LBUTTONDOWN;
    procedure WMSetFocus(var Message: TMessage); message WM_SETFOCUS;
  protected
    procedure BeforeChange; virtual;
    procedure MoveOut(var Key: Word; Shift: TShiftState); virtual;
    procedure Change; virtual;
    procedure CreateParams(var Params: TCreateParams); override;
    procedure Paint; override;
    property BorderStyle: TBorderStyle read GetBorderStyle write SetBorderStyle
             default bsSingle;
    property CellWidth: Integer read GetCellWidth write SetCellWidth
             default -1;
    property CurrencySymbol: String read GetCurrencySymbol write SetCurrencySymbol;
    property CurrencySymbolAligned: Boolean read GetCurrencySymbolAligned write SetCurrencySymbolAligned
             default False;
    property DecimalNumber: Integer read GetDecimalNumber write SetDecimalNumber
             default 2;
    property DecimalSeparatorColor: TColor read GetDecimalSeparatorColor write SetDecimalSeparatorColor
             default clRed;
    property DigitalNumber: Integer read GetDigitalNumber write SetDigitalNumber
             default 10;  { not clude dot }
    property FocusedColor: TColor read GetFocusedColor write SetFocusedColor
             default clYellow;
    property GridLineColor: TColor read GetGridLineColor write SetGridLineColor
             default clSilver;
    property GridLineWidth: Integer read GetGridLineWidth write SetGridLineWidth
             default 1;
    property KilobitSeparatorColor: TColor read GetKilobitSeparatorColor write SetKilobitSeparatorColor
             default clBlack;
    property MaxLength: Integer read GetMaxLength write SetMaxLength
             default FloatMaxLength; { not include dot }
    property MoveOutAllowed: Boolean read GetMoveOutAllowed write SetMoveOutAllowed
             default False;
    property NegativeColor: TColor read GetNegativeColor write SetNegativeColor
             default clRed;
    property NegativeFont: TFont read FNegativeFont write SetNegativeFont;
    property ReadOnly: Boolean read GetReadOnly write SetReadOnly
             default False;
    property ShowNegativeColor: Boolean read GetShowNegativeColor write SetShowNegativeColor
             default False;
    property ShowNegativeFont: Boolean read GetShowNegativeFont write SetShowNegativeFont
             default False;
    property ShowNegativeSign: Boolean read GetShowNegativeSign write SetShowNegativeSign
             default True;
    property TextLayout: TTextLayout read GetTextLayout write SetTextLayout
             default tlCenter;
    property Value: Extended read GetValue write SetValue;
    property ZeroEmpty: Boolean read GetZeroEmpty write SetZeroEmpty
             default True;
    property OnBeforeChange: TNotifyEvent read FOnBeforeChange write FOnBeforeChange;
    property OnMoveOut: TKeyEvent read FOnMoveOut write FOnMoveOut;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function Modified: Boolean;
    procedure UnDo;
  end;

  TsncCurrencyEdit = class(TsncCustomCurrencyEdit)
  published
    property BorderStyle;
    property CellWidth;
    property Color;
    property Ctl3D;
    property CurrencySymbol;
    property CurrencySymbolAligned;
    property DecimalNumber;
    property DecimalSeparatorColor;
    property DigitalNumber;
    property Enabled;
    property FocusedColor;
    property Font;
    property GridLineColor;
    property GridLineWidth;
    property KilobitSeparatorColor;
    property MaxLength;
    property MoveOutAllowed;
    property NegativeColor;
    property NegativeFont;
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property ReadOnly;
    property ShowHint;
    property ShowNegativeColor;
    property ShowNegativeFont;
    property ShowNegativeSign;
    property TabOrder;
    property TabStop;
    property TextLayout;
    property Value;
    property Visible;
    property ZeroEmpty;
    property OnBeforeChange;
    property OnMoveOut;
    property OnChange;
    property OnClick;
    property OnDblClick;
    property OnDragDrop;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
  end;

procedure DrawCurrencyFrame(DestCanvas: TCanvas; DestRect: TRect; CurrencyEdit: TsncCustomCurrencyEdit; DestValue: Extended);
procedure Register;

implementation

function sncStuff(S, Pattern: String; Index, Len: Integer): String;
var
  SLen, I: Integer;
  S1, S2: String;
begin
  Result := '';
  SLen := Length(S);
  if (Index<1) or (Len<0) or (Index>SLen+1) or (Index+Len>SLen+1) then
    Exit;
  S1 := '';
  S2 := '';
  for I := 1 to SLen do
    if (I<Index) then
      S1 := S1+S[I]
    else
    if (I>Index+Len-1) then
      S2 := S2+S[I];
  Result := S1 + Pattern + S2;
end;

procedure DrawCurrencyFrame(DestCanvas: TCanvas; DestRect: TRect; CurrencyEdit: TsncCustomCurrencyEdit; DestValue: Extended);
var
  DotLength: Integer;
  FormatString: String;
  WorkCellOffset, WorkCellWidth: Integer;
  DestHeight, DestWidth: Integer;

  I, Len: Integer;
  BrushColor: TColor;
  OldPenColor: TColor;
  OldPenWidth: Integer;
  OldPenPos: TPoint;
  OldBrushColor: TColor;
  OldFont: TFont;

  DestText: String;
  XOffset, YOffset: Integer;
  TheRect: TRect;
begin
  OldFont := TFont.Create;
  OldFont.Assign(DestCanvas.Font);
  OldBrushColor := DestCanvas.Brush.Color;

//  DestRect

  DestHeight := DestRect.Bottom-DestRect.Top;
  DestWidth := DestRect.Right-DestRect.Left;

  with CurrencyEdit do
  begin
    if (DecimalNumber=0) then
      DotLength := 0
    else
      DotLength := 1;
    FormatString := '0'+StringOfChar('.', DotLength)+StringOfChar('0', DecimalNumber);
    WorkCellWidth := (DestWidth-GridLineWidth*(DigitalNumber-1)) div DigitalNumber;
    WorkCellOffset := DestWidth-GridLineWidth*(DigitalNumber-1) - WorkCellWidth*DigitalNumber;

    if (DestValue<0) and (ShowNegativeColor) then
      BrushColor := NegativeColor
    else
      BrushColor := Color;
    DestCanvas.Brush.Color := BrushColor;
    DestCanvas.FillRect(DestRect);

    OldPenColor := DestCanvas.Pen.Color;
    OldPenWidth := DestCanvas.Pen.Width;
    OldPenPos := DestCanvas.PenPos;

    DestCanvas.Pen.Width := GridLineWidth;
    for I:=1 to DigitalNumber-1 do
    begin
      if ((DigitalNumber-DecimalNumber-I)=0) then
        DestCanvas.Pen.Color := DecimalSeparatorColor
      else
        if ((DigitalNumber-DecimalNumber-I) mod 3=0) then
          DestCanvas.Pen.Color := KilobitSeparatorColor
        else
          DestCanvas.Pen.Color := GridLineColor;
      DestCanvas.MoveTo(DestRect.Left+(WorkCellWidth+GridLineWidth)*I-GridLineWidth+WorkCellOffset, DestRect.Top);
      DestCanvas.LineTo(DestRect.Left+(WorkCellWidth+GridLineWidth)*I-GridLineWidth+WorkCellOffset, DestRect.Bottom);
    end;

    DestCanvas.Pen.Color := OldPenColor;
    DestCanvas.Pen.Width := OldPenWidth;
    DestCanvas.PenPos := OldPenPos;

    if (DestValue<0) and (ShowNegativeFont) then
      DestCanvas.Font.Assign(NegativeFont)
    else
      DestCanvas.Font.Assign(Font);

    if (ZeroEmpty and (DestValue=0)) then
    begin
      DestCanvas.FillRect(Rect(DestRect.Left,DestRect.Top,DestRect.Left+WorkCellWidth+WorkCellOffset-1,DestRect.Bottom));
      for I:=1 to DigitalNumber-1 do
        DestCanvas.FillRect(Rect(DestRect.Left+(WorkCellWidth+GridLineWidth)*I+WorkCellOffset,DestRect.Top,DestRect.Left+(WorkCellWidth+GridLineWidth)*I+WorkCellWidth+WorkCellOffset-1,DestRect.Bottom));
    end
    else begin
      if (DestValue<0) and not ShowNegativeSign then
        DestText := FormatFloat(FormatString, -DestValue)
      else
        DestText := FormatFloat(FormatString, DestValue);
      Len := Length(DestText);
      if (CurrencySymbol<>'') then
      begin
        if (DigitalNumber-(Len-DotLength)<1) then
        begin
          DestText := StringOfChar('*',DigitalNumber-DecimalNumber-1)+StringOfChar('.',DotLength)+StringOfChar('*',DecimalNumber);
          Len := DigitalNumber+DotLength-1;
        end;
      end
      else begin
        if (DigitalNumber-(Len-DotLength)<0) then
        begin
          DestText := StringOfChar('*',DigitalNumber-DecimalNumber)+StringOfChar('.',DotLength)+StringOfChar('*',DecimalNumber);
          Len := DigitalNumber+DotLength;
        end;
      end;
      case TextLayout of
        tlTop:    YOffset := 0;
        tlCenter: YOffset := (DestHeight-DestCanvas.TextHeight('0')) div 2+1;
        tlBottom: YOffset := DestHeight-DestCanvas.TextHeight('0');
      else
        YOffset := 0;
      end;

      if (CurrencySymbol<>'') then
      begin
        DestCanvas.FillRect(Rect(DestRect.Left,DestRect.Top,DestRect.Left+WorkCellWidth+WorkCellOffset,DestRect.Bottom));
        XOffset := (WorkCellWidth-DestCanvas.TextWidth(CurrencySymbol)) div 2;
        if CurrencySymbolAligned or (DigitalNumber-(Len-DotLength)=1) then
        begin
          TheRect := Rect(DestRect.Left,DestRect.Top,DestRect.Left+WorkCellWidth+WorkCellOffset,DestRect.Bottom);
          DestCanvas.TextRect(TheRect,DestRect.Left+XOffset+(WorkCellOffset div 2),DestRect.Top+YOffset, CurrencySymbol);
          for I:=1 to DigitalNumber-(Len-DotLength)-1 do
            DestCanvas.FillRect(Rect(DestRect.Left+(WorkCellWidth+GridLineWidth)*I+WorkCellOffset,DestRect.Top,DestRect.Left+(WorkCellWidth+GridLineWidth)*I+WorkCellWidth+WorkCellOffset,DestRect.Bottom));
        end
        else begin
          for I:=1 to DigitalNumber-(Len-DotLength)-1-1 do
            DestCanvas.FillRect(Rect(DestRect.Left+(WorkCellWidth+GridLineWidth)*I+WorkCellOffset,DestRect.Top,DestRect.Left+(WorkCellWidth+GridLineWidth)*I+WorkCellWidth+WorkCellOffset,DestRect.Bottom));
          TheRect := Rect(DestRect.Left+(WorkCellWidth+GridLineWidth)*(DigitalNumber-(Len-DotLength)-1)+WorkCellOffset,DestRect.Top,DestRect.Left+(WorkCellWidth+GridLineWidth)*(DigitalNumber-(Len-DotLength)-1)+WorkCellWidth+WorkCellOffset,DestRect.Bottom);
          DestCanvas.FillRect(TheRect);
          DestCanvas.TextRect(TheRect,DestRect.Left+(WorkCellWidth+GridLineWidth)*(DigitalNumber-(Len-DotLength)-1)+XOffset+WorkCellOffset,DestRect.Top+YOffset, CurrencySymbol);
        end;
      end
      else begin
        DestCanvas.FillRect(Rect(DestRect.Left,DestRect.Top,DestRect.Left+WorkCellWidth+WorkCellOffset,DestRect.Bottom));
        for I:=1 to DigitalNumber-(Len-DotLength)-1 do
          DestCanvas.FillRect(Rect(DestRect.Left+(WorkCellWidth+GridLineWidth)*I+WorkCellOffset,DestRect.Top,DestRect.Left+(WorkCellWidth+GridLineWidth)*I+WorkCellWidth+WorkCellOffset,DestRect.Bottom));
      end;

      XOffset := (WorkCellWidth-DestCanvas.TextWidth('0')) div 2;
      for I:=1 to Len-DecimalNumber-DotLength do
      begin
        TheRect := Rect(DestRect.Left+(WorkCellWidth+GridLineWidth)*(DigitalNumber-(Len-DotLength)-1+I)+WorkCellOffset,DestRect.Top,DestRect.Left+(WorkCellWidth+GridLineWidth)*(DigitalNumber-(Len-DotLength)-1+I)+WorkCellWidth+WorkCellOffset,DestRect.Bottom);
        DestCanvas.FillRect(TheRect);
        DestCanvas.TextRect(TheRect,DestRect.Left+(WorkCellWidth+GridLineWidth)*(DigitalNumber-(Len-DotLength)-1+I)+XOffset+WorkCellOffset,DestRect.Top+YOffset,DestText[I]);
      end;
      for I:=1 to DecimalNumber do
      begin
        TheRect := Rect(DestRect.Left+(WorkCellWidth+GridLineWidth)*(DigitalNumber-DecimalNumber-1+I)+WorkCellOffset,DestRect.Top,DestRect.Left+(WorkCellWidth+GridLineWidth)*(DigitalNumber-DecimalNumber-1+I)+WorkCellWidth+WorkCellOffset,DestRect.Bottom);
        DestCanvas.FillRect(TheRect);
        DestCanvas.TextRect(TheRect,DestRect.Left+(WorkCellWidth+GridLineWidth)*(DigitalNumber-DecimalNumber-1+I)+XOffset+WorkCellOffset,DestRect.Top+YOffset,DestText[Len-DecimalNumber+I]);
      end;
    end;
  end;

  DestCanvas.Brush.Color := OldBrushColor;
  DestCanvas.Font.Assign(OldFont);
  OldFont.Free;
end;

procedure Register;
begin
  RegisterComponents('SNC', [TsncCurrencyLabel]);
  RegisterComponents('SNC', [TsncCurrencyEdit]);
end;

{ TsncCurrencyLabel }

{ Method to set variable and property values and create objects }
procedure TsncCurrencyLabel.AutoInitialize;
begin
  BorderSize := 2;
  IntCellOffset := 0;
  IntCellWidth := 0;
  FBorderStyle := bsSingle;
  FCellWidth := -1;
  FCtl3D := True;
  FDecimalNumber := 2;
  FDecimalSeparatorColor := clRed;
  FDecimalSymbols := TStringList.Create;
  FDigitalNumber := 10;
  FDigitalSymbols := TStringList.Create;
  FGridLineColor := clSilver;
  FGridLineWidth := 1;
  FKilobitSeparatorColor := clBlack;
  FTextLayout := tlCenter;

  Width := 121;
  Height := 25;
  Color := clWindow;
  ParentColor := False;

  FDigitalSymbols.Add('Ôª');
  FDigitalSymbols.Add('Ê®');
  FDigitalSymbols.Add('°Ù');
  FDigitalSymbols.Add('Ç§');
  FDigitalSymbols.Add('Íò');
  FDigitalSymbols.Add('Ê®');
  FDigitalSymbols.Add('°Ù');
  FDigitalSymbols.Add('Ç§');
  FDigitalSymbols.Add('ÒÚ');
  FDigitalSymbols.Add('Ê®');
  FDigitalSymbols.Add('°Ù');
  FDigitalSymbols.Add('Ç§');
  FDigitalSymbols.Add('Íò');
  FDecimalSymbols.Add('½Ç');
  FDecimalSymbols.Add('·Ö');
end;

{ Method to free any objects created by AutoInitialize }
procedure TsncCurrencyLabel.AutoDestroy;
begin
  FDecimalSymbols.Free;
  FDigitalSymbols.Free;
end;

{ Read method for property BorderStyle }
function TsncCurrencyLabel.GetBorderStyle: TBorderStyle;
begin
  Result := FBorderStyle;
end;

{ Write method for property BorderStyle }
procedure TsncCurrencyLabel.SetBorderStyle(Value: TBorderStyle);
begin
  FBorderStyle := Value;

  if (FBorderStyle=bsNone) then
    BorderSize := 0
  else
    if Ctl3D then
      BorderSize := 2
    else
      BorderSize := 1;
  Invalidate;
end;

{ Read method for property CellWidth }
function TsncCurrencyLabel.GetCellWidth: Integer;
begin
  Result := FCellWidth;
end;

{ Write method for property CellWidth }
procedure TsncCurrencyLabel.SetCellWidth(Value: Integer);
begin
  FCellWidth := Value;

  Invalidate;
end;

{ Read method for property CTL3D }
function TsncCurrencyLabel.GetCtl3D: Boolean;
begin
  Result := FCtl3D;
end;

{ Write method for property CTL3D }
procedure TsncCurrencyLabel.SetCtl3D(Value: Boolean);
begin
  FCtl3D := Value;

  if (FBorderStyle=bsSingle) then
  begin
    if Ctl3D then
      BorderSize := 2
    else
      BorderSize := 1;
    Invalidate;
  end;
end;

{ Read method for property DecimalNumber }
function TsncCurrencyLabel.GetDecimalNumber: Integer;
begin
  Result := FDecimalNumber;
end;

{ Write method for property DecimalNumber }
procedure TsncCurrencyLabel.SetDecimalNumber(Value: Integer);
begin
  if (Value<0) or (Value>FDigitalNumber) then
    Exit;
  FDecimalNumber := Value;

  Invalidate;
end;

{ Read method for property DecimalSeparatorColor }
function TsncCurrencyLabel.GetDecimalSeparatorColor: TColor;
begin
  Result := FDecimalSeparatorColor;
end;

{ Write method for property DecimalSeparatorColor }
procedure TsncCurrencyLabel.SetDecimalSeparatorColor(Value: TColor);
begin
  FDecimalSeparatorColor := Value;

  Invalidate;
end;

{ Write method for property DecimalSymbols }
procedure TsncCurrencyLabel.SetDecimalSymbols(Value: TStrings);
begin
  FDecimalSymbols.Assign(Value);

  Invalidate;
end;

{ Read method for property DigitalNumber }
function TsncCurrencyLabel.GetDigitalNumber: Integer;
begin
  Result := FDigitalNumber;
end;

{ Write method for property DigitalNumber }
procedure TsncCurrencyLabel.SetDigitalNumber(Value: Integer);
begin
  if (Value<FDecimalNumber) then
    Exit;
  FDigitalNumber := Value;

  Invalidate;
end;

{ Write method for property DigitalSymbols }
procedure TsncCurrencyLabel.SetDigitalSymbols(Value: TStrings);
begin
  FDigitalSymbols.Assign(Value);

  Invalidate;
end;

{ Read method for property GridLineColor }
function TsncCurrencyLabel.GetGridLineColor: TColor;
begin
  Result := FGridLineColor;
end;

{ Write method for property GridLineColor }
procedure TsncCurrencyLabel.SetGridLineColor(Value: TColor);
begin
  FGridLineColor := Value;

  Invalidate;
end;

{ Read method for property GridLineWidth }
function TsncCurrencyLabel.GetGridLineWidth: Integer;
begin
  Result := FGridLineWidth;
end;

{ Write method for property GridLineWidth }
procedure TsncCurrencyLabel.SetGridLineWidth(Value: Integer);
begin
  FGridLineWidth := Value;

  Invalidate;
end;

{ Read method for property KilobitSeparatorColor }
function TsncCurrencyLabel.GetKilobitSeparatorColor: TColor;
begin
  Result := FKilobitSeparatorColor;
end;

{ Write method for property KilobitSeparatorColor }
procedure TsncCurrencyLabel.SetKilobitSeparatorColor(Value: TColor);
begin
  FKilobitSeparatorColor := Value;

  Invalidate;
end;

{ Read method for property TextLayout }
function TsncCurrencyLabel.GetTextLayout: TTextLayout;
begin
  Result := FTextLayout;
end;

{ Write method for property TextLayout }
procedure TsncCurrencyLabel.SetTextLayout(Value: TTextLayout);
begin
  FTextLayout := Value;

  Invalidate;
end;

constructor TsncCurrencyLabel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  AutoInitialize;
end;

destructor TsncCurrencyLabel.Destroy;
begin
  AutoDestroy;

  inherited Destroy;
end;

{ draw grid only }
procedure TsncCurrencyLabel.DrawGrid;
var
  I: Integer;
  IntCellWidth,IntCellOffset: Integer;
  OldPenColor: TColor;
  OldPenWidth: Integer;
  OldPenPos: TPoint;
  OldBrushColor: TColor;
begin
  if (FCellWidth>-1) then
  begin
    Width := FCellWidth*FDigitalNumber + FGridLineWidth*(FDigitalNumber-1) + BorderSize*2;
    IntCellWidth := FCellWidth;
    IntCellOffset := 0;
  end
  else begin
    IntCellWidth := (Width-FGridLineWidth*(FDigitalNumber-1)-BorderSize*2) div FDigitalNumber;
    IntCellOffset := Width-FGridLineWidth*(FDigitalNumber-1)-BorderSize*2 - IntCellWidth*FDigitalNumber;
  end;

  OldPenColor := Canvas.Pen.Color;
  OldPenWidth := Canvas.Pen.Width;
  OldPenPos := Canvas.PenPos;
  OldBrushColor := Canvas.Brush.Color;

  Canvas.Pen.Width := 1;
  if (FBorderStyle=bsSingle) and Ctl3D then
  begin
    Canvas.Pen.Color := clGray;
    Canvas.MoveTo(0,Height-1);
    Canvas.LineTo(0,0);
    Canvas.LineTo(Width,0);
    Canvas.Pen.Color := clBlack;
    Canvas.MoveTo(1,Height-2);
    Canvas.LineTo(1,1);
    Canvas.LineTo(Width-1,1);

    Canvas.Pen.Color := clWhite;
    Canvas.MoveTo(0,Height-1);
    Canvas.LineTo(Width-1,Height-1);
    Canvas.LineTo(Width-1,-1);
    Canvas.Pen.Color := clSilver;
    Canvas.MoveTo(0,Height-2);
    Canvas.LineTo(Width-2,Height-2);
    Canvas.LineTo(Width-2,0);

    Canvas.Brush.Color := Color;
    Canvas.FillRect(Rect(BorderSize, BorderSize, Width-BorderSize, Height-BorderSize));
  end
  else begin
    if (FBorderStyle=bsSingle) then
      Canvas.Pen.Color := clBlack
    else
      Canvas.Pen.Color := Color;
    Canvas.Brush.Color := Color;
    Canvas.Rectangle(0, 0, Width, Height);
  end;

  Canvas.Pen.Width := FGridLineWidth;
  for I:=1 to FDigitalNumber-1 do
  begin
    if ((FDigitalNumber-FDecimalNumber-I) = 0) then
      Canvas.Pen.Color := FDecimalSeparatorColor
    else
      if ((FDigitalNumber-FDecimalNumber-I) mod 3 = 0) then
        Canvas.Pen.Color := FKilobitSeparatorColor
      else
        Canvas.Pen.Color := FGridLineColor;
    Canvas.MoveTo((IntCellWidth+FGridLineWidth)*I-FGridLineWidth+BorderSize+IntCellOffset, BorderSize);
    Canvas.LineTo((IntCellWidth+FGridLineWidth)*I-FGridLineWidth+BorderSize+IntCellOffset, Height-BorderSize);
  end;

  Canvas.Pen.Color := OldPenColor;
  Canvas.Pen.Width := OldPenWidth;
  Canvas.PenPos := OldPenPos;
  Canvas.Brush.Color := OldBrushColor;
end;

{ draw text only }
procedure TsncCurrencyLabel.DrawText;
var
  I: Integer;
  XOffset, YOffset: Integer;
  TheRect: TRect;
begin
  if (FDigitalSymbols.Count<>0) or (FDecimalSymbols.Count<>0) then
  begin
    Canvas.Font.Assign(Font);
    Canvas.Brush.Color := Color;
    case FTextLayout of
      tlTop:    YOffset := 0;
      tlCenter: YOffset := (Height-BorderSize*2-Canvas.TextHeight(FDigitalSymbols.Strings[0])) div 2+1;
      tlBottom: YOffset := Height-BorderSize*2-Canvas.TextHeight(FDigitalSymbols.Strings[0]);
    else
      YOffset := 0;
    end;

    if (FDigitalSymbols.Count<>0) then
      for I:=0 to FDigitalNumber-FDecimalNumber-1 do
        if (FDigitalNumber-FDecimalNumber<=FDigitalSymbols.Count+I) then
        begin
          XOffset := (IntCellWidth-Canvas.TextWidth(FDigitalSymbols.Strings[FDigitalNumber-FDecimalNumber-1-I])) div 2;
          TheRect := Rect((IntCellWidth+FGridLineWidth)*I+BorderSize+IntCellOffset,BorderSize,(IntCellWidth+FGridLineWidth)*I+BorderSize+IntCellWidth+IntCellOffset,Height-BorderSize);
          Canvas.TextRect(TheRect,(IntCellWidth+FGridLineWidth)*I+BorderSize+XOffset+IntCellOffset,BorderSize+YOffset,FDigitalSymbols.Strings[FDigitalNumber-FDecimalNumber-1-I]);
        end;
    if (FDecimalSymbols.Count<>0) then
      for I:=0 to FDecimalNumber-1 do
        if (I<FDecimalSymbols.Count) then
        begin
          XOffset := (IntCellWidth-Canvas.TextWidth(DecimalSymbols.Strings[I])) div 2;
          TheRect := Rect((IntCellWidth+FGridLineWidth)*(FDigitalNumber-FDecimalNumber+I)+BorderSize+1+IntCellOffset,BorderSize+1,(IntCellWidth+FGridLineWidth)*(FDigitalNumber-FDecimalNumber+I)+BorderSize+IntCellWidth+IntCellOffset,Height-BorderSize);
          Canvas.TextRect(TheRect,(IntCellWidth+FGridLineWidth)*(FDigitalNumber-FDecimalNumber+I)+BorderSize+1+XOffset+IntCellOffset,BorderSize+1+YOffset,DecimalSymbols.Strings[I]);
        end;
  end;
end;

{ fully redraw control }
procedure TsncCurrencyLabel.Paint;
begin
  if (FCellWidth>-1) then
  begin
    IntCellWidth := FCellWidth;
    IntCellOffset := 0;
    Width := FCellWidth*FDigitalNumber + FGridLineWidth*(FDigitalNumber-1) + BorderSize*2;
  end
  else begin
    IntCellWidth := (Width-FGridLineWidth*(FDigitalNumber-1)-BorderSize*2) div FDigitalNumber;
    IntCellOffset := Width-FGridLineWidth*(FDigitalNumber-1)-BorderSize*2 - IntCellWidth*FDigitalNumber;
  end;

  DrawGrid;
  DrawText;
  inherited;
end;

{ TsncCustomCurrencyEdit }

{ Method to set variable and property values and create objects }
procedure TsncCustomCurrencyEdit.AutoInitialize;
begin
  FCursorVisible := False;
  FCursorWidth := 0;
  FCursorXPos := 0;
  FCursorY := 0;
  FDotLength := 1;
  FFormatString := '0.00';
  FWorkCellOffset := 0;
  FWorkCellWidth := 0;
  FNegativeSign := 1;
  FOriginValue := 0.00;
  FBorderStyle := bsSingle;
  FCellWidth := -1;
  FCurrencySymbol := '£¤';
  FCurrencySymbolAligned := False;
  FDecimalNumber := 2;
  FDecimalSeparatorColor := clRed;
  FDigitalNumber := 10;
  FFocusedColor := clYellow;
  FGridLineColor := clSilver;
  FGridLineWidth := 1;
  FKilobitSeparatorColor := clBlack;
  FMaxLength := FloatMaxLength;
  FMoveOutAllowed := False;
  FNegativeColor := clRed;
  FNegativeFont := TFont.Create;
  FReadOnly := False;
  FShowNegativeColor := False;
  FShowNegativeFont := False;
  FShowNegativeSign := True;
  FTextLayout := tlCenter;
  FValue := 0.00;
  FZeroEmpty := True;

  Width := 121;
  Height := 25;
  Color := clWindow;
  ParentColor := False;
  TabStop := True;

  FModified := false;
end;

{ Method to free any objects created by AutoInitialize }
procedure TsncCustomCurrencyEdit.AutoDestroy;
begin
  FNegativeFont.Free;
end;

{ Read method for property BorderStyle }
function TsncCustomCurrencyEdit.GetBorderStyle: TBorderStyle;
begin
  Result := FBorderStyle;
end;

{ Write method for property BorderStyle }
procedure TsncCustomCurrencyEdit.SetBorderStyle(Value: TBorderStyle);
begin
  if (FBorderStyle<>Value) then
  begin
    FBorderStyle := Value;
    RecreateWnd;
  end;
end;

{ Read method for property CellWidth }
function TsncCustomCurrencyEdit.GetCellWidth: Integer;
begin
  Result := FCellWidth;
end;

{ Write method for property CellWidth }
procedure TsncCustomCurrencyEdit.SetCellWidth(Value: Integer);
begin
  FCellWidth := Value;
  Invalidate;
end;

{ Read method for property CurrencySymbol }
function TsncCustomCurrencyEdit.GetCurrencySymbol: String;
begin
  Result := FCurrencySymbol;
end;

{ Write method for property CurrencySymbol }
procedure TsncCustomCurrencyEdit.SetCurrencySymbol(Value: String);
begin
  FCurrencySymbol := Value;
  DrawText;
end;

{ Read method for property CurrencySymbolAligned }
function TsncCustomCurrencyEdit.GetCurrencySymbolAligned: Boolean;
begin
  Result := FCurrencySymbolAligned;
end;

{ Write method for property CurrencySymbolAligned }
procedure TsncCustomCurrencyEdit.SetCurrencySymbolAligned(Value: Boolean);
begin
  FCurrencySymbolAligned := Value;
  DrawText;
end;

{ Read method for property DecimalNumber }
function TsncCustomCurrencyEdit.GetDecimalNumber: Integer;
begin
  Result := FDecimalNumber;
end;

{ Write method for property DecimalNumber }
procedure TsncCustomCurrencyEdit.SetDecimalNumber(Value: Integer);
begin
  if (Value<0) or (Value>=FDigitalNumber) then
    Exit;
  FDecimalNumber := Value;

  if (FDecimalNumber=0) then
    FDotLength := 0
  else
    FDotLength := 1;
  FFormatString := '0'+StringOfChar('.', FDotLength)+StringOfChar('0', FDecimalNumber);
  Invalidate;
end;

{ Read method for property DecimalSeparatorColor }
function TsncCustomCurrencyEdit.GetDecimalSeparatorColor: TColor;
begin
  Result := FDecimalSeparatorColor;
end;

{ Write method for property DecimalSeparatorColor }
procedure TsncCustomCurrencyEdit.SetDecimalSeparatorColor(Value: TColor);
begin
  FDecimalSeparatorColor := Value;
  Invalidate;
end;

{ Read method for property DigitalNumber }
function TsncCustomCurrencyEdit.GetDigitalNumber: Integer;
begin
  Result := FDigitalNumber;
end;

{ Write method for property DigitalNumber }
procedure TsncCustomCurrencyEdit.SetDigitalNumber(Value: Integer);
begin
  if (Value<FDecimalNumber) or (Value>FloatMaxLength) then
    Exit;
  FDigitalNumber := Value;
  Invalidate;
end;

{ Read method for property FocusedColor }
function TsncCustomCurrencyEdit.GetFocusedColor: TColor;
begin
  Result := FFocusedColor;
end;

{ Write method for property FocusedColor }
procedure TsncCustomCurrencyEdit.SetFocusedColor(Value: TColor);
begin
  FFocusedColor := Value;
  Invalidate;
end;

{ Read method for property GridLineColor }
function TsncCustomCurrencyEdit.GetGridLineColor: TColor;
begin
  Result := FGridLineColor;
end;

{ Write method for property GridLineColor }
procedure TsncCustomCurrencyEdit.SetGridLineColor(Value: TColor);
begin
  FGridLineColor := Value;
  Invalidate;
end;

{ Read method for property GridLineWidth }
function TsncCustomCurrencyEdit.GetGridLineWidth: Integer;
begin
  Result := FGridLineWidth;
end;

{ Write method for property GridLineWidth }
procedure TsncCustomCurrencyEdit.SetGridLineWidth(Value: Integer);
begin
  FGridLineWidth := Value;
  Invalidate;
end;

{ Read method for property KilobitSeparatorColor }
function TsncCustomCurrencyEdit.GetKilobitSeparatorColor: TColor;
begin
  Result := FKilobitSeparatorColor;
end;

{ Write method for property KilobitSeparatorColor }
procedure TsncCustomCurrencyEdit.SetKilobitSeparatorColor(Value: TColor);
begin
  FKilobitSeparatorColor := Value;
  Invalidate;
end;

{ Read method for property MaxLength }
function TsncCustomCurrencyEdit.GetMaxLength: Integer;
begin
  Result := FMaxLength;
end;

{ Write method for property MaxLength }
procedure TsncCustomCurrencyEdit.SetMaxLength(Value: Integer);
begin
  if (Value<0) or (Value>FloatMaxLength) then
    Exit;
  FMaxLength := Value;
end;

{ Read method for property MoveOutAllowed }
function TsncCustomCurrencyEdit.GetMoveOutAllowed: Boolean;
begin
  Result := FMoveOutAllowed;
end;

{ Write method for property MoveOutAllowed }
procedure TsncCustomCurrencyEdit.SetMoveOutAllowed(Value: Boolean);
begin
  FMoveOutAllowed := Value;
end;

{ Read method for property NegativeColor }
function TsncCustomCurrencyEdit.GetNegativeColor: TColor;
begin
  Result := FNegativeColor;
end;

{ Write method for property NegativeColor }
procedure TsncCustomCurrencyEdit.SetNegativeColor(Value: TColor);
begin
  FNegativeColor := Value;
  if (FNegativeSign=-1) then
    DrawText;
end;

{ Write method for property NegativeFont }
procedure TsncCustomCurrencyEdit.SetNegativeFont(Value: TFont);
begin
  FNegativeFont.Assign(Value);
  if (FNegativeSign=-1) then
    DrawText;
end;

{ Read method for property ReadOnly }
function TsncCustomCurrencyEdit.GetReadOnly: Boolean;
begin
  Result := FReadOnly;
end;

{ Write method for property ReadOnly }
procedure TsncCustomCurrencyEdit.SetReadOnly(Value: Boolean);
begin
  FReadOnly := Value;
end;

{ Read method for property ShowNegativeColor }
function TsncCustomCurrencyEdit.GetShowNegativeColor: Boolean;
begin
  Result := FShowNegativeColor;
end;

{ Write method for property ShowNegativeColor }
procedure TsncCustomCurrencyEdit.SetShowNegativeColor(Value: Boolean);
begin
  FShowNegativeColor := Value;
  if (FNegativeSign=-1) then
    DrawText;
end;

{ Read method for property ShowNegativeFont }
function TsncCustomCurrencyEdit.GetShowNegativeFont: Boolean;
begin
  Result := FShowNegativeFont;
end;

{ Write method for property ShowNegativeFont }
procedure TsncCustomCurrencyEdit.SetShowNegativeFont(Value: Boolean);
begin
  FShowNegativeFont := Value;
  if (FNegativeSign=-1) then
    DrawText;
end;

{ Read method for property ShowNegativeSign }
function TsncCustomCurrencyEdit.GetShowNegativeSign: Boolean;
begin
  Result := FShowNegativeSign;
end;

{ Write method for property ShowNegativeSign }
procedure TsncCustomCurrencyEdit.SetShowNegativeSign(Value: Boolean);
begin
  FShowNegativeSign := Value;
  if (FNegativeSign=-1) then
    DrawText;
end;

{ Read method for property TextLayout }
function TsncCustomCurrencyEdit.GetTextLayout: TTextLayout;
begin
  Result := FTextLayout;
end;

{ Write method for property TextLayout }
procedure TsncCustomCurrencyEdit.SetTextLayout(Value: TTextLayout);
begin
  FTextLayout := Value;
  DrawText;
end;

{ Read method for property Value }
function TsncCustomCurrencyEdit.GetValue: Extended;
begin
  Result := FValue*FNegativeSign;
end;

{ Write method for property Value }
procedure TsncCustomCurrencyEdit.SetValue(Value: Extended);
begin
  if (Value>=0) then begin
    FValue := Value;
    FNegativeSign := 1;
  end
  else begin
    FValue := -Value;
    FNegativeSign := -1;
  end;
  FOriginValue := Value;
  SyncCursorPos;
  DrawText;

  FModified := false;
end;

{ Read method for property ZeroEmpty }
function TsncCustomCurrencyEdit.GetZeroEmpty: Boolean;
begin
  Result := FZeroEmpty;
end;

{ Write method for property ZeroEmpty }
procedure TsncCustomCurrencyEdit.SetZeroEmpty(Value: Boolean);
begin
  FZeroEmpty := Value;
  DrawText;
end;

procedure TsncCustomCurrencyEdit.BeforeChange;
begin
  if Assigned(FOnBeforeChange) then
    FOnBeforeChange(Self);
end;

procedure TsncCustomCurrencyEdit.Change;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

{ when ctrl3d has changed, redraw control face }
procedure TsncCustomCurrencyEdit.CMCtl3DChanged(var Message: TMessage);
begin
  if NewStyleControls and (FBorderStyle = bsSingle) then
    RecreateWnd;
  inherited;
end;

{ when control get focus, set cusor pos at tail of integer }
procedure TsncCustomCurrencyEdit.CMEnter(var Message: TMessage);
begin
  inherited;
  FCursorXPos := FDigitalNumber-FDecimalNumber-1;
end;

{ when value has been modified and control has focus, set cursor pos for new value }
procedure TsncCustomCurrencyEdit.SyncCursorPos;
var
  OldCursorVisible: Boolean;
  Len: Integer;
begin
  OldCursorVisible := FCursorVisible;
  SetCursorState(false);
  Len := Length(FormatFloat(FFormatString, FValue));
  if (FCursorXPos<FDigitalNumber-(Len-FDotLength)-1) then
    FCursorXPos := FDigitalNumber-FDecimalNumber-1;
  SetCursorState(OldCursorVisible);
end;

{ when value has been modified , draw text only }
procedure TsncCustomCurrencyEdit.ChangeValue(Value: Extended);
begin
  if not FModified then
  begin
    BeforeChange;
    FModified := true;
  end;

  if (Value>=0) then
  begin
    FValue := Value;
    FNegativeSign := 1;
  end
  else begin
    FValue := -Value;
    FNegativeSign := -1;
  end;
  DrawText;
  Change;
end;

constructor TsncCustomCurrencyEdit.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  AutoInitialize;
end;

procedure TsncCustomCurrencyEdit.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do
  begin
    Style := Style or BorderStyles[FBorderStyle];
    if NewStyleControls and Ctl3D and (FBorderStyle = bsSingle) then
    begin
      Style := Style and not WS_BORDER;
      ExStyle := ExStyle or WS_EX_CLIENTEDGE;
    end;
  end;
end;

{ cursor flash timer handle }
procedure TsncCustomCurrencyEdit.CursorTimerHandle(Sender: TObject);
begin
  DrawCursor;
end;

{ calculate cursor pos }
procedure TsncCustomCurrencyEdit.DecodeCursorX(X: Integer);
var
  I, Len: Integer;
begin
  Len := Length(FormatFloat(FFormatString, FValue));
  for I:=0 to FDigitalNumber-1 do
    if (X>=(FWorkCellWidth+FGridLineWidth)*I+FWorkCellOffset) and
       (X<=(FWorkCellWidth+FGridLineWidth)*I+FWorkCellWidth-1+FWorkCellOffset) then
      begin
        if (I<FDigitalNumber-(Len-FDotLength)-1) then
          FCursorXPos := FDigitalNumber-(Len-FDotLength)-1
        else
          FCursorXPos := I;
        Break;
      end;
end;

destructor TsncCustomCurrencyEdit.Destroy;
begin
  AutoDestroy;
  inherited Destroy;
end;

{ draw flash cursor }
procedure TsncCustomCurrencyEdit.DrawCursor;
var
  OldPenColor: TColor;
  OldPenMode: TPenMode;
  CursorX: Integer;
begin
  CursorX := (FWorkCellWidth+FGridLineWidth)*FCursorXPos+FWorkCellOffset+((FWorkCellWidth-FCursorWidth) div 2);
  with Self.Canvas do
  begin
    OldPenColor := Pen.Color;
    OldPenMode := Pen.Mode;

    Pen.Color := Self.Color;
    Pen.Mode :=  pmXor;
    PolyLine([Point(CursorX, FCursorY), Point(CursorX+FCursorWidth-1, FCursorY),
              Point(CursorX+FCursorWidth-1, FCursorY+1),Point(CursorX-1, FCursorY+1)]);

    Pen.Color := OldPenColor;
    Pen.Mode :=  OldPenMode;
  end;
  FCursorVisible := not FCursorVisible;
end;

{ draw grid only }
procedure TsncCustomCurrencyEdit.DrawGrid;
var
  I: Integer;
  BrushColor: TColor;
  OldPenColor: TColor;
  OldPenWidth: Integer;
  OldPenPos: TPoint;
  OldBrushColor: TColor;
  OldCursorVisible: Boolean;
begin
  OldCursorVisible := FCursorVisible;
  SetCursorState(False);

  if Focused then
    BrushColor := FocusedColor
  else
    if (FNegativeSign=-1) and (ShowNegativeColor) then
      BrushColor := NegativeColor
    else
      BrushColor := Color;
  OldBrushColor := Canvas.Brush.Color;
  Canvas.Brush.Color := BrushColor;
  Canvas.FillRect(ClientRect);
  Canvas.Brush.Color := OldBrushColor;

  OldPenColor := Canvas.Pen.Color;
  OldPenWidth := Canvas.Pen.Width;
  OldPenPos := Canvas.PenPos;

  Canvas.Pen.Width := FGridLineWidth;
  for I:=1 to FDigitalNumber-1 do
  begin
    if ((FDigitalNumber-FDecimalNumber-I)=0) then
      Canvas.Pen.Color := FDecimalSeparatorColor
    else
      if ((FDigitalNumber-FDecimalNumber-I) mod 3=0) then
        Canvas.Pen.Color := FKilobitSeparatorColor
      else
        Canvas.Pen.Color := FGridLineColor;
    Canvas.MoveTo((FWorkCellWidth+FGridLineWidth)*I-FGridLineWidth+FWorkCellOffset, 0);
    Canvas.LineTo((FWorkCellWidth+FGridLineWidth)*I-FGridLineWidth+FWorkCellOffset, ClientHeight);
  end;

  Canvas.Pen.Color := OldPenColor;
  Canvas.Pen.Width := OldPenWidth;
  Canvas.PenPos := OldPenPos;

  SetCursorState(OldCursorVisible);
end;

{ draw grid only }
procedure TsncCustomCurrencyEdit.DrawText;
var
  I, Len: Integer;
  Text: String;
  XOffset, YOffset: Integer;
  TheRect: TRect;
  BrushColor: TColor;
  OldBrushColor: TColor;
  OldCursorVisible: Boolean;
begin
  OldCursorVisible := FCursorVisible;
  SetCursorState(False);

  OldBrushColor := Canvas.Brush.Color;
  if Focused then
    BrushColor := FocusedColor
  else
    if (FNegativeSign=-1) and (ShowNegativeColor) then
      BrushColor := NegativeColor
    else
      BrushColor := Color;
  Canvas.Brush.Color := BrushColor;

  if (FNegativeSign=-1) and (ShowNegativeFont) then
    Canvas.Font.Assign(NegativeFont)
  else
    Canvas.Font.Assign(Font);
  FCursorWidth := Canvas.TextWidth('0')+2;

  if (FZeroEmpty and (FValue=0) and not Focused) then
  begin
    Canvas.FillRect(Rect(0,0,FWorkCellWidth+FWorkCellOffset,ClientHeight));
    for I:=1 to FDigitalNumber-1 do
      Canvas.FillRect(Rect((FWorkCellWidth+FGridLineWidth)*I+FWorkCellOffset,0,(FWorkCellWidth+FGridLineWidth)*I+FWorkCellWidth+FWorkCellOffset,ClientHeight));
  end
  else begin
    if (FNegativeSign=-1) and (ShowNegativeSign) then
      Text := FormatFloat(FFormatString, Value)
    else
      Text := FormatFloat(FFormatString, FValue);
    Len := Length(Text);
    if (FCurrencySymbol<>'') then
    begin
      if (FDigitalNumber-(Len-FDotLength)<1) then
      begin
        Text := StringOfChar('*',FDigitalNumber-FDecimalNumber-1)+StringOfChar('.',FDotLength)+StringOfChar('*',FDecimalNumber);
        Len := FDigitalNumber+FDotLength-1;
      end;
    end
    else begin
      if (FDigitalNumber-(Len-FDotLength)<0) then
      begin
        Text := StringOfChar('*',FDigitalNumber-FDecimalNumber)+StringOfChar('.',FDotLength)+StringOfChar('*',FDecimalNumber);
        Len := FDigitalNumber+FDotLength;
      end;
    end;
    case FTextLayout of
      tlTop:    YOffset := 0;
      tlCenter: YOffset := (ClientHeight-Canvas.TextHeight('0')) div 2+1;
      tlBottom: YOffset := ClientHeight-Canvas.TextHeight('0');
    else
      YOffset := 0;
    end;
    FCursorY := YOffset+Canvas.TextHeight('0')+0;
    if (FCursorY+1>=ClientHeight) then
      FCursorY := ClientHeight-2;

    if (FCurrencySymbol<>'') then
    begin
      Canvas.FillRect(Rect(0,0,FWorkCellWidth+FWorkCellOffset,ClientHeight));
      XOffset := (FWorkCellWidth-Canvas.TextWidth(FCurrencySymbol)) div 2;
      if FCurrencySymbolAligned or (FDigitalNumber-(Len-FDotLength)=1) then
      begin
        TheRect := Rect(0,0,FWorkCellWidth+FWorkCellOffset,ClientHeight);
        Canvas.TextRect(TheRect,XOffset+(FWorkCellOffset div 2),YOffset, FCurrencySymbol);
        for I:=1 to FDigitalNumber-(Len-FDotLength)-1 do
          Canvas.FillRect(Rect((FWorkCellWidth+FGridLineWidth)*I+FWorkCellOffset,0,(FWorkCellWidth+FGridLineWidth)*I+FWorkCellWidth+FWorkCellOffset,ClientHeight));
      end
      else begin
        for I:=1 to FDigitalNumber-(Len-FDotLength)-1-1 do
          Canvas.FillRect(Rect((FWorkCellWidth+FGridLineWidth)*I+FWorkCellOffset,0,(FWorkCellWidth+FGridLineWidth)*I+FWorkCellWidth+FWorkCellOffset,ClientHeight));
        TheRect := Rect((FWorkCellWidth+FGridLineWidth)*(FDigitalNumber-(Len-FDotLength)-1)+FWorkCellOffset,0,(FWorkCellWidth+FGridLineWidth)*(FDigitalNumber-(Len-FDotLength)-1)+FWorkCellWidth+FWorkCellOffset,ClientHeight);
        Canvas.FillRect(TheRect);
        Canvas.TextRect(TheRect,(FWorkCellWidth+FGridLineWidth)*(FDigitalNumber-(Len-FDotLength)-1)+XOffset+FWorkCellOffset,YOffset, FCurrencySymbol);
      end;
    end
    else begin
      Canvas.FillRect(Rect(0,0,FWorkCellWidth+FWorkCellOffset,ClientHeight));
      for I:=1 to FDigitalNumber-(Len-FDotLength)-1 do
        Canvas.FillRect(Rect((FWorkCellWidth+FGridLineWidth)*I+FWorkCellOffset,0,(FWorkCellWidth+FGridLineWidth)*I+FWorkCellWidth+FWorkCellOffset,ClientHeight));
    end;
    XOffset := (FWorkCellWidth-Canvas.TextWidth('0')) div 2;
    for I:=1 to Len-FDecimalNumber-FDotLength do
    begin
      TheRect := Rect((FWorkCellWidth+FGridLineWidth)*(FDigitalNumber-(Len-FDotLength)-1+I)+FWorkCellOffset,0,(FWorkCellWidth+FGridLineWidth)*(FDigitalNumber-(Len-FDotLength)-1+I)+FWorkCellWidth+FWorkCellOffset,ClientHeight);
      Canvas.FillRect(TheRect);
      Canvas.TextRect(TheRect,(FWorkCellWidth+FGridLineWidth)*(FDigitalNumber-(Len-FDotLength)-1+I)+XOffset+FWorkCellOffset,YOffset,Text[I]);
    end;
    for I:=1 to FDecimalNumber do
    begin
      TheRect := Rect((FWorkCellWidth+FGridLineWidth)*(FDigitalNumber-FDecimalNumber-1+I)+FWorkCellOffset,0,(FWorkCellWidth+FGridLineWidth)*(FDigitalNumber-FDecimalNumber-1+I)+FWorkCellWidth+FWorkCellOffset,ClientHeight);
      Canvas.FillRect(TheRect);
      Canvas.TextRect(TheRect,(FWorkCellWidth+FGridLineWidth)*(FDigitalNumber-FDecimalNumber-1+I)+XOffset+FWorkCellOffset,YOffset,Text[Len-FDecimalNumber+I]);
    end;
  end;

  Canvas.Brush.Color := OldBrushColor;
  SetCursorState(OldCursorVisible);
end;

{ calculate the cursor pos in first time of get focus}
procedure TsncCustomCurrencyEdit.InitCursorX(X: Integer);
var
  I, Len: Integer;
begin
  FCursorXPos := FDigitalNumber-FDecimalNumber-1;
  Len := Length(FormatFloat(FFormatString, FValue));
  for I:=0 to FDigitalNumber-1 do
    if (X>=(FWorkCellWidth+FGridLineWidth)*I+FWorkCellOffset) and
       (X<=(FWorkCellWidth+FGridLineWidth)*I+FWorkCellWidth-1+FWorkCellOffset) then
      begin
        if (I<FDigitalNumber-(Len-FDotLength)) then
          FCursorXPos := FDigitalNumber-(Len-FDotLength)
        else
          FCursorXPos := I;
        Break;
      end;
end;

{ get the modify state of control}
function TsncCustomCurrencyEdit.Modified: Boolean;
begin
  Result := FModified;
end;

{ fully redraw control }
procedure TsncCustomCurrencyEdit.Paint;
begin
  if (FCellWidth>-1) then
  begin
    ClientWidth := FCellWidth*FDigitalNumber + FGridLineWidth*(FDigitalNumber-1);
    FWorkCellWidth := FCellWidth;
    FWorkCellOffset := 0;
  end
  else begin
    FWorkCellWidth := (ClientWidth-FGridLineWidth*(FDigitalNumber-1)) div FDigitalNumber;
    FWorkCellOffset := ClientWidth-FGridLineWidth*(FDigitalNumber-1) - FWorkCellWidth*FDigitalNumber;
  end;

  DrawGrid;
  DrawText;
  inherited;
end;

{set cursor visible or not}
procedure TsncCustomCurrencyEdit.SetCursorState(Visible: Boolean);
begin
  if (Visible and FCursorVisible) or (not Visible and not FCursorVisible) then
    Exit;
  DrawCursor;
end;

{undo change}
procedure TsncCustomCurrencyEdit.UnDo;
begin
  Value := FOriginValue;
end;

procedure TsncCustomCurrencyEdit.WMChar(var Message: TMessage);
var
  Text: String;
  Len: Integer;
  OldCursorVisible: Boolean;
begin
  with TWMKey(Message) do
    case CharCode of
      Ord('0')..Ord('9'):
        begin
          Text := FormatFloat(FFormatString, FValue);
          Len := Length(Text);
          if not FReadOnly and (Len-FDotLength-FDecimalNumber<FMaxLength) then
          begin
            OldCursorVisible := FCursorVisible;
            SetCursorState(false);
            Text := sncStuff(Text, Chr(CharCode), FCursorXPos-(FDigitalNumber-1-(Len-FDotLength)-1), 0);
            if (FDecimalNumber>0) and (Len+1-Pos('.',Text)>FDecimalNumber) then
              Text := sncStuff(Text, '', Len+1, 1);
            if (FCursorXPos>=FDigitalNumber-FDecimalNumber) and (FCursorXPos<FDigitalNumber-1) then
              FCursorXPos := FCursorXPos+1;
            SetCursorState(OldCursorVisible);
            ChangeValue(StrToFloat(Text)*FNegativeSign);
          end;
        end;
    end;

  inherited;
end;

procedure TsncCustomCurrencyEdit.WMKeyDown(var Message: TMessage);
var
  ShiftState: TShiftState;
  Text: String;
  Len: Integer;
  OldCursorVisible: Boolean;
  IntCharCode: Word;
begin
  with TWMKey(Message) do
  begin
    ShiftState := KeyDataToShiftState(KeyData);
    IntCharCode := CharCode;
    if (ShiftState=[ssShift]) and (CharCode=187) then
      IntCharCode := VK_ADD;
    if (ShiftState=[]) and (CharCode=188) then
      IntCharCode := VK_SEPARATOR;
    if (ShiftState=[]) and (CharCode=189) then
      IntCharCode := VK_SUBTRACT;
    if (ShiftState=[]) and (CharCode=190) then
      IntCharCode := VK_DECIMAL;

    Text := FormatFloat(FFormatString, FValue);
    Len := Length(Text);

    case IntCharCode of
      VK_SUBTRACT:
        if not FReadOnly then
          ChangeValue(-Value);
      VK_ADD:
        if not FReadOnly and (Value<0) then
          ChangeValue(-Value);
      VK_BACK:
        if not FReadOnly then
        begin
          OldCursorVisible := FCursorVisible;
          SetCursorState(false);
          if (FCursorXPos<FDigitalNumber-FDecimalNumber) then
            if (FCursorXPos>FDigitalNumber-(Len-FDotLength)) then
              Text := sncStuff(Text, '', FCursorXPos-(FDigitalNumber-(Len-FDotLength)), 1)
            else begin
              if (FCursorXPos=FDigitalNumber-(Len-FDotLength)) then
                Text := sncStuff(Text, '', FCursorXPos-(FDigitalNumber-(Len-FDotLength)-1), 1);
              if (FCursorXPos<FDigitalNumber-FDecimalNumber-1) then
                FCursorXPos := FCursorXPos+1;
            end
          else begin
            if (FCursorXPos>FDigitalNumber-FDecimalNumber) then
              Text := sncStuff(Text, '', FCursorXPos-(FDigitalNumber-Length(Text)-FDotLength)-1, 1)
            else
              Text := sncStuff(Text, '', FCursorXPos-(FDigitalNumber-Length(Text)-FDotLength), 1);
            if (FCursorXPos>FDigitalNumber-FDecimalNumber) then
              FCursorXPos := FCursorXPos-1;
          end;
          if (Text='') then
            Text := '0';
          SetCursorState(OldCursorVisible);
          ChangeValue(StrToFloat(Text)*FNegativeSign);
        end;
      VK_DELETE:
        if not FReadOnly then
          if (ShiftState=[]) then
          begin
            OldCursorVisible := FCursorVisible;
            SetCursorState(false);
            if (FCursorXPos<FDigitalNumber-FDecimalNumber) then
            begin
              if (FCursorXPos>=FDigitalNumber-(Len-FDotLength)) then
                Text := sncStuff(Text, '', FCursorXPos-(FDigitalNumber-(Len-FDotLength)-1), 1);
              if (FCursorXPos<FDigitalNumber-FDecimalNumber-1) then
                FCursorXPos := FCursorXPos+1;
            end
            else
              Text := sncStuff(Text, '', FCursorXPos-(FDigitalNumber-Length(Text)-FDotLength), 1);
            if (Text='') then
              Text := '0';
            SetCursorState(OldCursorVisible);
            ChangeValue(StrToFloat(Text)*FNegativeSign);
          end
          else
            if (ShiftState=[ssCtrl]) then
            begin
              OldCursorVisible := FCursorVisible;
              SetCursorState(false);
              FCursorXPos := FDigitalNumber-FDecimalNumber-1;
              Text := '0';
              SetCursorState(OldCursorVisible);
              ChangeValue(StrToFloat(Text)*FNegativeSign);
            end;
      VK_DECIMAL:
        if (FDecimalNumber<>0) then
        begin
          SetCursorState(false);
          if (FCursorXPos>=FDigitalNumber-FDecimalNumber) then
            FCursorXPos := FDigitalNumber-FDecimalNumber-1
          else
            FCursorXPos := FDigitalNumber-FDecimalNumber;
          SetCursorState(true);
        end;
    end;
  end;

  inherited;
end;

procedure TsncCustomCurrencyEdit.CNKEYDOWN(var Message: TMessage);
var
  ShiftState: TShiftState;
  Offset, Len: Integer;
  CanMoveOut: Boolean;
begin
  CanMoveOut := false;
  with TWMKey(Message) do
  begin
    ShiftState := KeyDataToShiftState(KeyData);
    Len := Length(FormatFloat(FFormatString, FValue));
    case CharCode of
      VK_LEFT:
        if (ShiftState=[ssCtrl]) then
        begin
          if (FCursorXPos>FDigitalNumber-(Len-FDotLength)-1) then
          begin
            SetCursorState(false);
            if (FCursorXPos<FDigitalNumber-FDecimalNumber) then
            begin
              Offset := FCursorXPos-3+(FDigitalNumber-FDecimalNumber-FCursorXPos-1) mod 3;
              if (Offset>=FDigitalNumber-(Len-FDotLength)-1) then
                FCursorXPos := Offset
              else
                FCursorXPos := FDigitalNumber-(Len-FDotLength)-1;
            end
            else
              if (FCursorXPos=FDigitalNumber-FDecimalNumber) then
                FCursorXPos := FCursorXPos-1
              else begin
                Offset := FCursorXPos-1-(FCursorXPos-1-(FDigitalNumber-FDecimalNumber)) mod 3;
//                if (Offset>=FDigitalNumber-FDecimalNumber) then
                FCursorXPos := Offset;
              end;
            SetCursorState(true);
          end
          else
            CanMoveOut := FMoveOutAllowed;
        end
        else
          if (FCursorXPos>FDigitalNumber-(Len-FDotLength)-1) then begin
            SetCursorState(false);
            FCursorXPos := FCursorXPos-1;
            SetCursorState(true);
          end
          else
            CanMoveOut := FMoveOutAllowed;
      VK_RIGHT:
        if (ShiftState=[ssCtrl]) then
        begin
          if (FCursorXPos<FDigitalNumber-1) then
          begin
            SetCursorState(false);
            if (FCursorXPos<FDigitalNumber-FDecimalNumber-1) then
            begin
              Offset := FCursorXPos+1+(FDigitalNumber-FDecimalNumber-FCursorXPos+1) mod 3;
//              if (Offset<FDigitalNumber-FDecimalNumber) then
               FCursorXPos := Offset;
            end
            else
              if (FCursorXPos=FDigitalNumber-FDecimalNumber-1) then
                FCursorXPos := FCursorXPos+1
              else begin
                Offset := FCursorXPos+3-(FCursorXPos-(FDigitalNumber-FDecimalNumber)) mod 3;
                if (Offset<=FDigitalNumber-1) then
                  FCursorXPos := Offset
                else
                  FCursorXPos := FDigitalNumber-1;
              end;
            SetCursorState(true);
          end
          else
            CanMoveOut := FMoveOutAllowed;
        end
        else
          if (FCursorXPos<FDigitalNumber-1) then
          begin
            SetCursorState(false);
            FCursorXPos := FCursorXPos+1;
            SetCursorState(true);
          end
          else
            CanMoveOut := FMoveOutAllowed;
      VK_UP,
      VK_DOWN:
        CanMoveOut := FMoveOutAllowed;
      VK_HOME:
        begin
          SetCursorState(false);
          FCursorXPos := FDigitalNumber-(Len-FDotLength)-1;
          SetCursorState(true);
        end;
      VK_END:
        begin
          SetCursorState(false);
          FCursorXPos := FDigitalNumber-1;
          SetCursorState(true);
        end;
      else
        begin
          inherited;
          Exit;
        end;
    end;
  end;
  if CanMoveOut then
  begin
    inherited;
    MoveOut(TWMKey(Message).CharCode, ShiftState);
  end;
end;

procedure TsncCustomCurrencyEdit.MoveOut(var Key: Word; Shift: TShiftState);
begin
  if Assigned(FOnMoveOut) then
    FOnMoveOut(Self, Key, Shift);
end;

procedure TsncCustomCurrencyEdit.WMKillFocus(var Message: TMessage);
begin
  inherited;

  FCursorTimer.Enabled := False;
  FCursorTimer.Free;
  FCursorTimer := nil;
  SetCursorState(False);

  Repaint;
end;

procedure TsncCustomCurrencyEdit.WMLButtonDown(var Message: TMessage);
begin
  inherited;

  if not Focused then
  begin
    SetFocus;
    SetCursorState(False);
    InitCursorX(TWMMouse(Message).Pos.X);
    SetCursorState(True);
  end
  else begin
    SetCursorState(False);
    DecodeCursorX(TWMMouse(Message).Pos.X);
    SetCursorState(True);
  end;
end;

procedure TsncCustomCurrencyEdit.WMSetFocus(var Message: TMessage);
begin
  inherited;

  Repaint;

  FCursorTimer := TTimer.Create(nil);
  FCursorTimer.Enabled := False;
  FCursorTimer.interval := 500;
  FCursorTimer.OnTimer := CursorTimerHandle;
  FCursorTimer.Enabled := True;
  FCursorVisible := False;
  SetCursorState(True);
end;

end.
