Componentes.Terceros.jvcl/official/3.32/run/JvValidateEdit.pas

1472 lines
46 KiB
ObjectPascal

{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is JvValidateEdit, released on 20 February 2003,
by Christopher Latta
Portions created by Christopher Latta are Copyright (C) 2003 Christopher Latta.
All Rights Reserved.
Contributor(s): Peter Thornqvist
Peter Schraut (http://www.console-dev.de)
You may retrieve the latest version of this file at the Project JEDI's JVCL
home page, located at http://jvcl.sourceforge.net
Known Issues:
TJvValidateFormat uses the SysUtils.Format function to format numeric values.
While this uses the Windows regional settings for the currency symbol, decimal
separator and thousands separator, it does not format using the negative symbol,
negative number format, negative currency format and positive currency format.
This could be rectified by a custom-written formatting routine.
-----------------------------------------------------------------------------}
// $Id: JvValidateEdit.pas 11040 2006-11-25 15:49:51Z marquardt $
unit JvValidateEdit;
{$I jvcl.inc}
interface
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
Windows, Messages, Controls, Graphics,
SysUtils, Classes,
JvEdit, JvDataSourceIntf;
type
TJvValidateEditDisplayFormat = (dfAlphabetic, dfAlphaNumeric, dfBinary,
dfCheckChars, dfCurrency, dfCustom, dfFloat, dfFloatGeneral, dfHex, dfInteger,
dfNonCheckChars, dfNone, dfOctal, dfPercent, dfScientific, dfYear);
TJvValidateEditCriticalPointsCheck = (cpNone, cpMinValue, cpMaxValue, cpBoth);
TJvCustomValidateEdit = class;
TJvValidateEditDataConnector = class(TJvFieldDataConnector)
private
FEdit: TJvCustomValidateEdit;
FNullValue: Variant;
procedure SetNullValue(const Value: Variant);
function IsNullValueStored: Boolean;
protected
procedure RecordChanged; override;
procedure UpdateData; override;
public
constructor Create(AEdit: TJvCustomValidateEdit);
procedure Assign(Source: TPersistent); override;
property Control: TJvCustomValidateEdit read FEdit;
published
property NullValue: Variant read FNullValue write SetNullValue stored IsNullValueStored;
end;
TJvValidateEditCriticalPoints = class(TPersistent)
private
FCheckPoints: TJvValidateEditCriticalPointsCheck;
FColorAbove: TColor;
FColorBelow: TColor;
FMaxValue: Double;
FMinValue: Double;
FMaxValueIncluded: Boolean;
FMinValueIncluded: Boolean;
FOnChange: TNotifyEvent;
FDefCheckPoints: TJvValidateEditCriticalPointsCheck;
FDefColorAbove: TColor;
FDefColorBelow: TColor;
procedure DoChanged;
procedure SetMinValue(NewValue: Double);
procedure SetMaxValue(NewValue: Double);
procedure SetColorAbove(NewValue: TColor);
procedure SetColorBelow(NewValue: TColor);
procedure SetCheckPoints(NewValue: TJvValidateEditCriticalPointsCheck);
function IsCheckPointsStored: Boolean;
function IsColorAboveStored: Boolean;
function IsColorBelowStored: Boolean;
public
procedure Assign(Source: TPersistent); override;
procedure SetDefaults(ACheckPoints: TJvValidateEditCriticalPointsCheck;
AColorAbove, AColorBelow: TColor);
constructor Create;
published
property CheckPoints: TJvValidateEditCriticalPointsCheck read FCheckPoints
write SetCheckPoints stored IsCheckPointsStored;
property ColorAbove: TColor read FColorAbove write SetColorAbove stored IsColorAboveStored;
property ColorBelow: TColor read FColorBelow write SetColorBelow stored IsColorBelowStored;
property MaxValue: Double read FMaxValue write SetMaxValue;
property MinValue: Double read FMinValue write SetMinValue;
property MaxValueIncluded: Boolean read FMaxValueIncluded write FMaxValueIncluded;
property MinValueIncluded: Boolean read FMinValueIncluded write FMinValueIncluded;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;
TJvCustomTextValidateEvent = procedure(Sender: TObject; Key: Char;
const AText: string; const Pos: Integer; var IsValid: Boolean) of object;
TJvCustomIsValidEvent = procedure(Sender: TObject; var IsValid: Boolean) of object;
TJvCustomValidateEdit = class(TJvCustomEdit)
private
FSelfChange: Boolean;
FCheckChars: string;
FDecimalPlaces: Cardinal;
FDisplayFormat: TJvValidateEditDisplayFormat;
FEditText: string;
FHasMaxValue: Boolean;
FHasMinValue: Boolean;
FMaxValue: Double;
FMinValue: Double;
FOnCustomValidate: TJvCustomTextValidateEvent;
FOnValueChanged: TNotifyEvent;
FZeroEmpty: Boolean;
EnterText: string;
FDisplayPrefix: string;
FDisplaySuffix: string;
FCriticalPoints: TJvValidateEditCriticalPoints;
FStandardFontColor: TColor;
FAutoAlignment: Boolean;
FTrimDecimals: Boolean;
FOldFontChange: TNotifyEvent;
FOnIsValid: TJvCustomIsValidEvent;
FAllowEmpty: Boolean;
FEnforcingMinMaxValue: Boolean;
procedure DisplayText;
function ScientificStrToFloat(SciString: string): Double;
procedure SetHasMaxValue(NewValue: Boolean);
procedure SetHasMinValue(NewValue: Boolean);
procedure SetMaxValue(NewValue: Double);
procedure SetMinValue(NewValue: Double);
procedure SetDecimalPlaces(NewValue: Cardinal);
procedure SetDisplayFormat(NewValue: TJvValidateEditDisplayFormat);
procedure SetZeroEmpty(NewValue: Boolean);
function GetAsInteger: Int64;
procedure SetAsInteger(NewValue: Int64);
function GetAsCurrency: Currency;
procedure SetAsCurrency(NewValue: Currency);
function GetAsFloat: Double;
procedure SetAsFloat(NewValue: Double);
function GetValue: Variant;
procedure SetValue(NewValue: Variant);
procedure SetCheckChars(const NewValue: string);
function IsCheckCharsStored: Boolean;
function CurrRangeValue(CheckValue: Currency): Currency; overload;
function FloatRangeValue(CheckValue: Double): Double; overload;
function IntRangeValue(CheckValue: Int64): Int64; overload;
function GetEditText: string;
procedure SetEditText(const NewValue: string);
procedure ChangeText(const NewValue: string);
function BaseToInt(const BaseValue: string; Base: Byte): Int64;
function IntToBase(NewValue: Int64; Base: Byte): string;
procedure DoValueChanged;
procedure SetDisplayPrefix(const NewValue: string);
procedure SetDisplaySuffix(const NewValue: string);
procedure CriticalPointsChange(Sender: TObject);
procedure SetFontColor;
procedure FontChange(Sender: TObject);
procedure EnforceMaxValue;
procedure EnforceMinValue;
procedure SetTrimDecimals(const Value: Boolean);
protected
function IsValidChar(const S: string; var Key: Char; Posn: Integer): Boolean; virtual;
function MakeValid(const ParseString: string): string;virtual;
procedure Change; override;
procedure FocusKilled(NextWnd: THandle); override;
procedure FocusSet(PrevWnd: THandle); override;
procedure WMPaste(var Msg: TMessage); message WM_PASTE;
procedure SetText(const NewValue: TCaption); override;
property CheckChars: string read FCheckChars write SetCheckChars
stored IsCheckCharsStored;
property TrimDecimals: Boolean read FTrimDecimals write SetTrimDecimals;
property DecimalPlaces: Cardinal read FDecimalPlaces write SetDecimalPlaces;
property DisplayFormat: TJvValidateEditDisplayFormat read FDisplayFormat
write SetDisplayFormat;
property EditText: string read GetEditText write SetEditText;
property HasMaxValue: Boolean read FHasMaxValue write SetHasMaxValue;
property HasMinValue: Boolean read FHasMinValue write SetHasMinValue;
property MaxValue: Double read FMaxValue write SetMaxValue;
property MinValue: Double read FMinValue write SetMinValue;
property OnCustomValidate: TJvCustomTextValidateEvent
read FOnCustomValidate write FOnCustomValidate;
property OnValueChanged: TNotifyEvent read FOnValueChanged write FOnValueChanged;
property Value: Variant read GetValue write SetValue stored False;
property AllowEmpty: Boolean read FAllowEmpty write FAllowEmpty;
property ZeroEmpty: Boolean read FZeroEmpty write SetZeroEmpty;
property DisplayPrefix: string read FDisplayPrefix write SetDisplayPrefix;
property DisplaySuffix: string read FDisplaySuffix write SetDisplaySuffix;
property CriticalPoints: TJvValidateEditCriticalPoints read FCriticalPoints
write FCriticalPoints;
property AutoAlignment: Boolean read FAutoAlignment write FAutoAlignment;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure KeyPress(var Key: Char); override;
function DoValidate(const Key: Char; const AText: string;
const Posn: Integer): Boolean;
procedure Loaded; override;
function CreateDataConnector: TJvFieldDataConnector; override;
property OnIsValid: TJvCustomIsValidEvent read FOnIsValid write FOnIsValid;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function IsValid: Boolean; virtual; // fires OnIsValid if assigned
procedure Assign(Source: TPersistent); override;
property AsInteger: Int64 read GetAsInteger write SetAsInteger;
property AsCurrency: Currency read GetAsCurrency write SetAsCurrency;
property AsFloat: Double read GetAsFloat write SetAsFloat;
end;
TJvValidateEdit = class(TJvCustomValidateEdit)
published
property AllowEmpty default False;
property Alignment default taRightJustify;
property Anchors;
property AutoAlignment default True;
property AutoSelect;
property AutoSize;
{$IFDEF VCL}
property BiDiMode;
property DragCursor;
property DragKind;
property Flat;
property ImeMode;
property ImeName;
property OEMConvert;
property ParentBiDiMode;
property ParentFlat;
property OnEndDock;
property OnStartDock;
{$IFDEF COMPILER6_UP}
property BevelEdges;
property BevelInner;
property BevelKind default bkNone;
property BevelOuter;
{$ENDIF COMPILER6_UP}
{$ENDIF VCL}
property BorderStyle;
property Caret;
property CheckChars;
property CharCase;
property ClipboardCommands;
property Color;
property Constraints;
property CriticalPoints;
property DisabledColor;
property DisabledTextColor;
property TrimDecimals default False;
property DisplayFormat default dfInteger;
property DecimalPlaces default 0;
property DisplayPrefix;
property DisplaySuffix;
property DragMode;
property EditText;
property Enabled;
property Font;
property HasMaxValue default False;
property HasMinValue default False;
property HideSelection;
property MaxLength;
property MaxValue;
property MinValue;
property ParentColor;
property ParentFont;
property ParentShowHint;
property PasswordChar;
property PopupMenu;
property ReadOnly;
property ShowHint;
property TabOrder;
property TabStop;
property Text stored False;
property Value;
property Visible;
property ZeroEmpty default False;
property OnChange;
property OnClick;
property OnContextPopup;
property OnCustomValidate;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDrag;
property OnValueChanged;
property OnIsValid;
property DataConnector;
end;
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvValidateEdit.pas $';
Revision: '$Revision: 11040 $';
Date: '$Date: 2006-11-25 16:49:51 +0100 (sam., 25 nov. 2006) $';
LogPath: 'JVCL\run'
);
{$ENDIF UNITVERSIONING}
implementation
uses
Math,
{$IFDEF HAS_UNIT_VARIANTS}
VarUtils, Variants,
{$ELSE}
ActiveX,
{$ENDIF HAS_UNIT_VARIANTS}
JclStrings, JvJCLUtils, JvResources;
function IsGreater(Value, MaxValue: Double; MaxValueIncluded: Boolean): Boolean;
begin
if MaxValueIncluded then
Result := Value >= MaxValue
else
Result := Value > MaxValue;
end;
function IsLower(Value, MinValue: Double; MinValueIncluded: Boolean): Boolean;
begin
if MinValueIncluded then
Result := Value <= MinValue
else
Result := Value < MinValue;
end;
//=== { TJvValidateEditDataConnector } =======================================
procedure TJvValidateEditDataConnector.Assign(Source: TPersistent);
begin
inherited Assign(Source);
if Source is TJvFieldDataConnector then
begin
NullValue := TJvValidateEditDataConnector(Source).NullValue;
end;
end;
constructor TJvValidateEditDataConnector.Create(AEdit: TJvCustomValidateEdit);
begin
inherited Create;
FEdit := AEdit;
VarClear(FNullValue);
end;
function TJvValidateEditDataConnector.IsNullValueStored: Boolean;
begin
{$IFDEF COMPILER6_UP}
Result := not VarIsClear(NullValue);
{$ELSE}
Result := not VarIsEmpty(NullValue);
{$ENDIF COMPILER6_UP}
end;
procedure TJvValidateEditDataConnector.RecordChanged;
begin
if Field.IsValid then
begin
FEdit.ReadOnly := not Field.CanModify;
if not Field.IsNull then
FEdit.Value := Field.Value
else
if NullValue <> Null then
FEdit.Value := NullValue
else
FEdit.Text := '';
end
else
begin
FEdit.Text := '';
FEdit.ReadOnly := False;
end;
end;
procedure TJvValidateEditDataConnector.SetNullValue(const Value: Variant);
begin
if Value <> FNullValue then
begin
FNullValue := Value;
Reset;
end;
end;
procedure TJvValidateEditDataConnector.UpdateData;
begin
if Field.CanModify and Field.IsValid then
begin
if FEdit.Value <> Null then
Field.Value := FEdit.Value
else
if NullValue <> Null then
Field.Value := FNullValue
else
RecordChanged;
end;
end;
//=== { TJvCustomValidateEdit } ==============================================
constructor TJvCustomValidateEdit.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FSelfChange := False;
FAutoAlignment := True;
FCriticalPoints := TJvValidateEditCriticalPoints.Create;
FCriticalPoints.OnChange := CriticalPointsChange;
FDisplayFormat := dfInteger;
FCheckChars := '01234567890';
Alignment := taRightJustify;
FEditText := '';
Text := '';
AutoSize := True;
FMinValue := 0;
FMaxValue := 0;
FHasMinValue := False;
FHasMaxValue := False;
FZeroEmpty := False;
FStandardFontColor := Font.Color;
FOldFontChange := Font.OnChange;
Font.OnChange := FontChange;
end;
destructor TJvCustomValidateEdit.Destroy;
begin
FreeAndNil(FCriticalPoints);
inherited Destroy;
end;
procedure TJvCustomValidateEdit.Assign(Source: TPersistent);
var
lcSource: TJvCustomValidateEdit;
begin
if Source is TJvCustomValidateEdit then
begin
lcSource := TJvCustomValidateEdit(Source);
CriticalPoints.Assign(lcSource.CriticalPoints);
DisplayFormat := lcSource.DisplayFormat;
DecimalPlaces := lcSource.DecimalPlaces;
MinValue := lcSource.MinValue;
MaxValue := lcSource.MaxValue;
HasMinValue := lcSource.HasMinValue;
HasMaxValue := lcSource.HasMaxValue;
ZeroEmpty := lcSource.ZeroEmpty;
AllowEmpty := lcSource.AllowEmpty;
end
else
inherited Assign(Source);
end;
procedure TJvCustomValidateEdit.Loaded;
begin
inherited Loaded;
// (obones) Why is this necessary? It overrides DecimalPlaces set to 0 by the user
{ if DisplayFormat = dfCurrency then
if FDecimalPlaces = 0 then
FDecimalPlaces := CurrencyDecimals;}
DataConnector.Active := False;
try
EditText := FEditText;
finally
DataConnector.Active := True;
end;
end;
function TJvCustomValidateEdit.CreateDataConnector: TJvFieldDataConnector;
begin
Result := TJvValidateEditDataConnector.Create(Self);
end;
procedure TJvCustomValidateEdit.SetHasMaxValue(NewValue: Boolean);
begin
if FHasMaxValue <> NewValue then
begin
FHasMaxValue := NewValue;
if not (csLoading in ComponentState) then
EnforceMaxValue;
end;
end;
procedure TJvCustomValidateEdit.SetHasMinValue(NewValue: Boolean);
begin
if FHasMinValue <> NewValue then
begin
FHasMinValue := NewValue;
if not (csLoading in ComponentState) then
EnforceMinValue;
end;
end;
procedure TJvCustomValidateEdit.SetMaxValue(NewValue: Double);
begin
if FMaxValue <> NewValue then
begin
FMaxValue := NewValue;
{ make MinValue consistent }
if FMinValue > FMaxValue then
FMinValue := FMaxValue;
if not (csLoading in ComponentState) then
EnforceMaxValue;
end;
end;
procedure TJvCustomValidateEdit.SetMinValue(NewValue: Double);
begin
if FMinValue <> NewValue then
begin
FMinValue := NewValue;
{ make MaxValue consistent }
if FMaxValue < FMinValue then
FMaxValue := FMinValue;
if not (csLoading in ComponentState) then
EnforceMinValue;
end;
end;
procedure TJvCustomValidateEdit.SetTrimDecimals(const Value: Boolean);
begin
if Value <> FTrimDecimals then
begin
FTrimDecimals := Value;
if not (csLoading in ComponentState) then
EditText := FEditText;
end;
end;
procedure TJvCustomValidateEdit.SetDecimalPlaces(NewValue: Cardinal);
begin
if ControlState = [csReadingState] then
FDecimalPlaces := NewValue
else
if FDisplayFormat in [dfCurrency, dfFloat, dfFloatGeneral, dfScientific, dfPercent] then
FDecimalPlaces := NewValue;
if not (csLoading in ComponentState) then
EditText := FEditText;
end;
procedure TJvCustomValidateEdit.SetDisplayFormat(NewValue: TJvValidateEditDisplayFormat);
const
Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
Numbers = '0123456789';
var
OldFormat: TJvValidateEditDisplayFormat;
begin
if FDisplayFormat <> NewValue then
begin
OldFormat := FDisplayFormat;
FDisplayFormat := NewValue;
case FDisplayFormat of
dfAlphabetic:
begin
FCheckChars := Alphabet;
if FAutoAlignment then
Alignment := taLeftJustify;
end;
dfAlphaNumeric:
begin
FCheckChars := Alphabet + Numbers;
if FAutoAlignment then
Alignment := taLeftJustify;
end;
dfBinary:
begin
FCheckChars := '01';
if FAutoAlignment then
Alignment := taRightJustify;
end;
dfCheckChars, dfNonCheckChars:
if FAutoAlignment then
Alignment := taLeftJustify;
dfCustom, dfNone:
begin
if (FDisplayFormat = dfCustom) or not (csLoading in ComponentState) then
FCheckChars := '';
if FAutoAlignment then
Alignment := taLeftJustify;
end;
dfCurrency:
begin
FCheckChars := Numbers + DecimalSeparator;
if FAutoAlignment then
Alignment := taRightJustify;
if not (csLoading in ComponentState) then
if FDecimalPlaces = 0 then
FDecimalPlaces := CurrencyDecimals;
end;
dfFloat, dfFloatGeneral, dfPercent:
begin
FCheckChars := Numbers + DecimalSeparator;
if FAutoAlignment then
Alignment := taRightJustify;
end;
dfHex:
begin
FCheckChars := Numbers + 'ABCDEFabcdef';
if FAutoAlignment then
Alignment := taRightJustify;
end;
dfInteger:
begin
FCheckChars := Numbers;
if FAutoAlignment then
Alignment := taRightJustify;
end;
dfOctal:
begin
FCheckChars := '01234567';
if FAutoAlignment then
Alignment := taRightJustify;
end;
dfScientific:
begin
FCheckChars := Numbers + 'Ee' + DecimalSeparator;
if FAutoAlignment then
Alignment := taRightJustify;
end;
dfYear:
begin
FCheckChars := Numbers;
if FAutoAlignment then
Alignment := taRightJustify;
MaxLength := 4;
end;
end;
if OldFormat = dfYear then
MaxLength := 0;
// Convert non-base 10 numbers to base 10 and base-10 numbers to non-base 10
if (OldFormat = dfBinary) and
(NewValue in [dfCurrency, dfFloat, dfFloatGeneral, dfHex, dfInteger, dfOctal, dfPercent, dfScientific, dfYear]) then
SetAsInteger(BaseToInt(FEditText, 2))
else
if (OldFormat in [dfCurrency, dfFloat, dfFloatGeneral, dfPercent]) and
(NewValue in [dfBinary, dfHex, dfOctal]) then
SetAsFloat(StrToFloatDef(FEditText, 0))
else
if (OldFormat = dfHex) and
(NewValue in [dfBinary, dfCurrency, dfFloat, dfFloatGeneral, dfInteger, dfOctal, dfPercent, dfScientific, dfYear]) then
SetAsInteger(BaseToInt(FEditText, 16))
else
if (OldFormat in [dfInteger, dfYear]) and
(NewValue in [dfBinary, dfHex, dfOctal]) then
SetAsInteger(StrToIntDef(FEditText, 0))
else
if (OldFormat = dfOctal) and
(NewValue in [dfBinary, dfCurrency, dfFloat, dfFloatGeneral, dfHex, dfInteger, dfPercent, dfScientific, dfYear]) then
SetAsInteger(BaseToInt(FEditText, 8))
else
begin
// ...or just display the value
if not (csLoading in ComponentState) then
EditText := FEditText;
end;
end;
end;
procedure TJvCustomValidateEdit.SetZeroEmpty(NewValue: Boolean);
begin
if FZeroEmpty <> NewValue then
begin
FZeroEmpty := NewValue;
if not (csLoading in ComponentState) then
EditText := FEditText;
end;
end;
function TJvCustomValidateEdit.GetAsInteger: Int64;
begin
case FDisplayFormat of
dfBinary:
Result := BaseToInt(FEditText, 2);
dfHex:
Result := BaseToInt(FEditText, 16);
dfOctal:
Result := BaseToInt(FEditText, 8);
else
Result := StrToInt64Def(FEditText, 0);
end;
end;
procedure TJvCustomValidateEdit.SetAsInteger(NewValue: Int64);
begin
case FDisplayFormat of
dfAlphabetic, dfAlphaNumeric, dfCheckChars, dfCustom,
dfNonCheckChars, dfNone:
EditText := IntToStr(NewValue);
dfBinary:
EditText := IntToBase(NewValue, 2);
dfHex:
EditText := IntToBase(NewValue, 16);
dfOctal:
EditText := IntToBase(NewValue, 8);
dfCurrency, dfFloat, dfFloatGeneral, dfInteger, dfPercent, dfScientific, dfYear:
EditText := IntToStr(IntRangeValue(NewValue));
end;
end;
function TJvCustomValidateEdit.GetAsCurrency: Currency;
begin
case FDisplayFormat of
dfBinary:
Result := BaseToInt(FEditText, 2);
dfHex:
Result := BaseToInt(FEditText, 16);
dfOctal:
Result := BaseToInt(FEditText, 8);
else
Result := StrToCurrDef(FEditText, 0);
end;
end;
procedure TJvCustomValidateEdit.SetAsCurrency(NewValue: Currency);
begin
case FDisplayFormat of
dfAlphabetic, dfAlphaNumeric, dfCheckChars, dfCustom,
dfNonCheckChars, dfNone:
EditText := CurrToStr(NewValue);
dfBinary:
EditText := IntToBase(Trunc(NewValue), 2);
dfHex:
EditText := IntToBase(Trunc(NewValue), 16);
dfOctal:
EditText := IntToBase(Trunc(NewValue), 8);
dfCurrency, dfFloat, dfFloatGeneral, dfInteger, dfPercent, dfScientific, dfYear:
EditText := CurrToStr(CurrRangeValue(NewValue));
end;
end;
function TJvCustomValidateEdit.GetAsFloat: Double;
var
Cur: Currency;
begin
case FDisplayFormat of
dfBinary:
Result := BaseToInt(FEditText, 2);
dfHex:
Result := BaseToInt(FEditText, 16);
dfOctal:
Result := BaseToInt(FEditText, 8);
dfScientific:
Result := ScientificStrToFloat(FEditText);
dfCurrency:
begin
// Mantis 3494: The Edit text may contain extra characters such as
// parenthesis that indicate the amount is negative. Using StrToFloatDef
// would not catch the negative part, hence the need to use a function
// that knows how to do the conversion.
VarCyFromStr(FEditText, LOCALE_USER_DEFAULT, 0, Cur);
Result := Cur;
end;
else
Result := StrToFloatDef(FEditText, 0);
end;
end;
procedure TJvCustomValidateEdit.SetAsFloat(NewValue: Double);
begin
case FDisplayFormat of
dfAlphabetic, dfAlphaNumeric, dfCheckChars, dfCustom,
dfNonCheckChars, dfNone:
EditText := FloatToStr(NewValue);
dfBinary:
EditText := IntToBase(Trunc(NewValue), 2);
dfHex:
EditText := IntToBase(Trunc(NewValue), 16);
dfOctal: EditText := IntToBase(Trunc(NewValue), 8);
dfInteger, dfYear:
EditText := IntToStr(IntRangeValue(Trunc(NewValue)));
dfCurrency:
EditText := Format('%.*m', [FDecimalPlaces, FloatRangeValue(NewValue)]);
dfFloat, dfPercent:
EditText := Format('%.*n', [FDecimalPlaces, FloatRangeValue(NewValue)]);
dfFloatGeneral:
EditText := Format('%.*g', [FDecimalPlaces, FloatRangeValue(NewValue)]);
dfScientific:
EditText := Format('%e', [FloatRangeValue(NewValue)]);
end;
end;
function TJvCustomValidateEdit.GetValue: Variant;
var
DisplayedText : string;
Cur: Currency;
begin
case FDisplayFormat of
dfCurrency:
begin
// Mantis 3494: The Edit text may contain extra characters such as
// parenthesis that indicate the amount is negative. Using StrToFloatDef
// would not catch the negative part, hence the need to use a function
// that knows how to do the conversion.
VarCyFromStr(FEditText, LOCALE_USER_DEFAULT, 0, Cur);
Result := Cur;
end;
dfFloat, dfFloatGeneral, dfPercent, dfScientific:
Result := StrToFloatDef(FEditText, 0);
dfInteger, dfYear:
Result := StrToIntDef(FEditText, 0);
dfHex:
Result := StrToIntDef('$' + FEditText, 0);
else
begin
DisplayedText := inherited Text;
// Remove DisplayPrefix and DisplaySuffix
DisplayedText := StrEnsureNoPrefix(DisplayPrefix, DisplayedText);
DisplayedText := StrEnsureNoSuffix(DisplaySuffix, DisplayedText);
Result := DisplayedText;
end;
end;
end;
procedure TJvCustomValidateEdit.SetValue(NewValue: Variant);
begin
case FDisplayFormat of
dfAlphabetic, dfAlphaNumeric, dfCheckChars, dfNonCheckChars, dfNone, dfCustom:
EditText := NewValue;
dfBinary, dfHex, dfInteger, dfOctal, dfYear:
{$IFDEF COMPILER5}
SetAsInteger(Integer(NewValue));
{$ELSE}
SetAsInteger(NewValue);
{$ENDIF COMPILER5}
dfCurrency, dfFloat, dfFloatGeneral, dfPercent, dfScientific:
SetAsFloat(NewValue);
end;
end;
procedure TJvCustomValidateEdit.SetCheckChars(const NewValue: string);
begin
if (csLoading in ComponentState) or
((FDisplayFormat in [dfNone, dfCheckChars, dfNonCheckChars]) and
(FCheckChars <> NewValue)) then
begin
FCheckChars := NewValue;
EditText := MakeValid(FEditText);
end;
end;
function TJvCustomValidateEdit.IsCheckCharsStored: Boolean;
begin
Result := (FDisplayFormat in [dfNone, dfCheckChars, dfNonCheckChars]);
end;
procedure TJvCustomValidateEdit.KeyPress(var Key: Char);
begin
if not IsValidChar(Text, Key, SelStart + 1) and (Key >= #32) then
Key := #0;
inherited KeyPress(Key);
end;
procedure TJvCustomValidateEdit.WMPaste(var Msg: TMessage);
begin
inherited;
EditText := MakeValid(inherited Text);
end;
function TJvCustomValidateEdit.MakeValid(const ParseString: string): string;
var
C: Char;
I: Integer;
L: Integer;
begin
SetLength(Result, Length(ParseString));
L := 0;
for I := 1 to Length(ParseString) do
begin
C := ParseString[I];
if IsValidChar(Copy(ParseString, 1, I - 1), C, I) then
begin
Result[L+1] := C;
Inc(L);
end;
end;
SetLength(Result, L);
end;
function TJvCustomValidateEdit.IsValidChar(const S: string;
var Key: Char; Posn: Integer): Boolean;
var
iPosE: Integer;
ExpectedNegPos: Integer;
ExpectedNegChar: Char;
begin
case FDisplayFormat of
dfBinary, dfCheckChars, dfHex, dfOctal, dfYear:
Result := Pos(Key, FCheckChars) > 0;
dfAlphabetic:
Result := IsCharAlpha(Key);
dfAlphaNumeric:
Result := IsCharAlphaNumeric(Key);
dfCustom:
Result := DoValidate(Key, S, Posn);
dfInteger:
Result := (Pos(Key, FCheckChars) > 0) or
((Key = '+') and (Posn = 1) and ((Pos('+', S) = 0) or (SelLength > 0))) or
((Key = '-') and (Posn = 1) and ((Pos('-', S) = 0) or (SelLength > 0)));
dfFloat, dfFloatGeneral, dfPercent:
Result := (Pos(Key, FCheckChars) > 0) or
((Key = DecimalSeparator) and (Pos(DecimalSeparator, S) = 0)) or
((Key = '+') and (Posn = 1) and ((Pos('+', S) = 0) or (SelLength > 0))) or
((Key = '-') and (Posn = 1) and ((Pos('-', S) = 0) or (SelLength > 0)));
dfCurrency:
begin
// The currency negative format can be quite complicated. The current
// one is indicated by the value of NegCurrFormat, and can have any
// value from 0 to 15 according to the MSDN and Delphi's help.
// So we must take into account that some format require the negative
// sign to be at the end, while some others replace it by parenthesis.
// See http://www.delphibasics.co.uk/RTL.asp?Name=NegCurrFormat for
// an online version of Delphi's help.
// If we were not to use this, it would trigger Mantis 3494, where
// the number would go from negative to positive simply by focusing out
// of the control.
ExpectedNegChar := '-';
ExpectedNegPos := 1;
case NegCurrFormat of
0, 4, 14, 15:
begin
ExpectedNegPos := 1;
ExpectedNegChar := '(';
end;
1, 5, 8, 9:
ExpectedNegPos := 1;
2:
ExpectedNegPos := 2;
3, 7, 10, 11:
ExpectedNegPos := Length(S);
6:
ExpectedNegPos := Length(S)-1;
12:
ExpectedNegPos := 3;
13:
ExpectedNegPos := Length(S)-2;
end;
if (Key = '(') and (Posn = 1) and (NegCurrFormat in [0, 4, 14, 15]) then
Key := '-';
Result := (Pos(Key, FCheckChars) > 0) or
((Key = DecimalSeparator) and (Pos(DecimalSeparator, S) = 0)) or
((Key = '+') and (Posn = 1) and ((Pos('+', S) = 0) or (SelLength > 0))) or
((Key = '-') and (Posn = ExpectedNegPos) and ((Pos(ExpectedNegChar, S) = 0) or (SelLength > 0)));
end;
dfNonCheckChars:
Result := Pos(Key, FCheckChars) = 0;
dfNone:
Result := True;
dfScientific:
begin
Result := (Pos(Key, FCheckChars) > 0) or (Key in ['+', '-']);
if Result then
begin
iPosE := Pos('e', LowerCase(S));
if Key = DecimalSeparator then
begin
if iPosE = 0 then
Result := (Pos(DecimalSeparator, S) = 0)
else
Result := ((Posn <= iPosE) and (Pos(DecimalSeparator, Copy(S, 1, iPosE - 1)) = 0));
//or ((Posn > iPosE) and (Pos(DecimalSeparator, Copy(S, iPosE + 1, Length(S))) = 0));
// (outchy) XXXeY,YY are not valid scientific numbers, Y must be an integer value
end
else
if Key in ['E', 'e'] then
Result := (iPosE = 0) and (Posn > 1)
else
if Key = '+' then
Result := (Posn = 1) or (Posn = iPosE + 1)
else
if Key = '-' then
Result := (Posn = 1) or (Posn = iPosE + 1);
end;
end;
else
Result := False;
end;
end;
function TJvCustomValidateEdit.DoValidate(const Key: Char;
const AText: string; const Posn: Integer): Boolean;
begin
Result := True;
if Assigned(FOnCustomValidate) then
FOnCustomValidate(Self, Key, AText, Posn, Result);
end;
procedure TJvCustomValidateEdit.KeyDown(var Key: Word; Shift: TShiftState);
begin
// if Key = VK_DELETE then EditText := MakeValid(inherited Text);
if Key = VK_ESCAPE then
begin
Key := 0;
EditText := EnterText;
SelStart := 0;
SelLength := Length(FEditText);
end;
inherited KeyDown(Key, Shift);
end;
function TJvCustomValidateEdit.CurrRangeValue(CheckValue: Currency): Currency;
begin
Result := CheckValue;
if FHasMaxValue and (CheckValue > FMaxValue) then
Result := FMaxValue
else
if FHasMinValue and (CheckValue < FMinValue) then
Result := FMinValue;
end;
function TJvCustomValidateEdit.FloatRangeValue(CheckValue: Double): Double;
begin
Result := CheckValue;
if FHasMaxValue and (CheckValue > FMaxValue) then
Result := FMaxValue
else
if FHasMinValue and (CheckValue < FMinValue) then
Result := FMinValue;
end;
function TJvCustomValidateEdit.IntRangeValue(CheckValue: Int64): Int64;
begin
Result := CheckValue;
if FHasMaxValue and (CheckValue > FMaxValue) then
Result := Trunc(FMaxValue)
else
if FHasMinValue and (CheckValue < FMinValue) then
Result := Trunc(FMinValue);
end;
function TJvCustomValidateEdit.GetEditText: string;
begin
Result := FEditText;
end;
procedure TJvCustomValidateEdit.SetEditText(const NewValue: string);
begin
FEditText := MakeValid(NewValue);
if (FDisplayFormat = dfYear) and ((not FHasMaxValue) or
(FHasMaxValue and (FMaxValue > 2000 + TwoDigitYearCenturyWindow))) and
((MaxLength = 0) or (MaxLength > 3)) then
FEditText := IntToStr(MakeYear4Digit(StrToIntDef(FEditText, 0), TwoDigitYearCenturyWindow));
if (FDisplayFormat in [dfBinary, dfCurrency, dfFloat, dfFloatGeneral, dfHex, dfInteger,
dfOctal, dfPercent, dfScientific, dfYear]) then
begin
EnforceMaxValue;
EnforceMinValue;
end;
// ChangeText(FEditText);
DisplayText;
DoValueChanged;
end;
procedure TJvCustomValidateEdit.FocusSet(PrevWnd: THandle);
begin
DisplayText;
inherited FocusSet(PrevWnd);
end;
procedure TJvCustomValidateEdit.FocusKilled(NextWnd: THandle);
var
DisplayedText: string;
begin
if not (csDestroying in ComponentState) then
begin
DisplayedText := inherited Text;
DisplayedText := StrEnsureNoPrefix(DisplayPrefix, DisplayedText);
DisplayedText := StrEnsureNoSuffix(DisplaySuffix, DisplayedText);
EditText := DisplayedText;
end;
inherited FocusKilled(NextWnd);
end;
procedure TJvCustomValidateEdit.ChangeText(const NewValue: string);
var
S, Exponent: string;
Ps, I: Integer;
begin
FSelfChange := True;
try
Ps := 0;
if TrimDecimals then
begin
I := Pos('e', LowerCase(NewValue));
if (DisplayFormat = dfScientific) and (I <> 0) then
begin
Exponent := Copy(NewValue, I, Length(NewValue));
Dec(I);
end else
begin
Exponent := '';
I := Length(NewValue);
end;
Ps := Pos(DecimalSeparator, NewValue);
if Ps > 0 then
begin
while (I > Ps) and (NewValue[I] = '0') do
Dec(I);
if Ps = I then
Dec(I); // skip decimal separator (Ivo Bauer)
S := FDisplayPrefix + Copy(NewValue, 1, I) + Exponent + FDisplaySuffix;
end;
end;
if Ps = 0 then
S := FDisplayPrefix + NewValue + FDisplaySuffix;
if S <> inherited Text then
inherited SetText(S);
finally
FSelfChange := False;
end;
end;
procedure TJvCustomValidateEdit.DisplayText;
begin
// The number types need to be formatted
if FAllowEmpty and (FEditText = '') then
ChangeText('')
else
if (FDisplayFormat in [dfBinary, dfCurrency, dfFloat, dfFloatGeneral, dfInteger, dfOctal, dfPercent, dfScientific, dfYear]) and
(AsFloat = 0) and FZeroEmpty then
ChangeText('')
else
begin
case FDisplayFormat of
dfCurrency:
ChangeText(Format('%.*m', [FDecimalPlaces, AsCurrency]));
dfInteger:
ChangeText(IntToStr(AsInteger));
dfFloat:
ChangeText(Format('%.*n', [FDecimalPlaces, AsFloat]));
dfFloatGeneral:
ChangeText(Format('%.*g', [FDecimalPlaces, AsFloat]));
dfScientific:
ChangeText(Format('%.*e', [FDecimalPlaces, AsFloat]));
dfPercent:
ChangeText(Format('%.*n%', [FDecimalPlaces, AsFloat]));
else
ChangeText(FEditText);
end;
// This needs to be done AFTER the text has been changed so that the color
// is directly shown correctly. (Mantis 3493)
if (FCriticalPoints.CheckPoints <> cpNone) and
(FDisplayFormat in [dfBinary, dfCurrency, dfFloat, dfFloatGeneral, dfHex, dfInteger, dfOctal, dfPercent, dfScientific, dfYear]) then
SetFontColor;
end;
end;
function TJvCustomValidateEdit.ScientificStrToFloat(SciString: string): Double;
var
I: Cardinal;
sMantissa, sExponent: string;
bInExp: Boolean;
begin
if Pos('E', UpperCase(SciString)) = 0 then
Result := StrToFloatDef(SciString, 0)
else
begin
sMantissa := '';
sExponent := '';
bInExp := False;
for I := 1 to Length(SciString) do
begin
if UpperCase(SciString[I]) = 'E' then
bInExp := True
else
begin
if bInExp then
sExponent := sExponent + SciString[I]
else
sMantissa := sMantissa + SciString[I];
end;
end;
Result := StrToFloatDef(sMantissa, 0) * Power(10, StrToFloatDef(sExponent, 0));
end;
end;
function TJvCustomValidateEdit.BaseToInt(const BaseValue: string; Base: Byte): Int64;
begin
Assert(Base <= 36, RsEBaseTooBig);
Assert(Base > 1, RsEBaseTooSmall);
Result := Numb2Dec(BaseValue, Base);
end;
function TJvCustomValidateEdit.IntToBase(NewValue:Int64; Base: Byte): string;
begin
Assert(Base <= 36, RsEBaseTooBig);
Assert(Base > 1, RsEBaseTooSmall);
Result := Dec2Numb(NewValue, 0, Base);
end;
procedure TJvCustomValidateEdit.DoValueChanged;
begin
try
if Assigned(FOnValueChanged) and (EnterText <> FEditText) then
FOnValueChanged(Self);
finally
EnterText := FEditText;
end;
end;
procedure TJvCustomValidateEdit.Change;
var
DisplayedText: string;
begin
// Update FEditText for User changes, so that the AsInteger, etc,
// functions work while editing
if not FSelfChange then
begin
DisplayedText := inherited Text;
DisplayedText := StrEnsureNoPrefix(DisplayPrefix, DisplayedText);
DisplayedText := StrEnsureNoSuffix(DisplaySuffix, DisplayedText);
FEditText := DisplayedText;
end;
inherited Change;
end;
procedure TJvCustomValidateEdit.SetText(const NewValue: TCaption);
begin
// If we are actually changing our value ourselves, there is no need
// to do it again. This may even trigger an infinite recursion, especially
// when in a derived component the display format is set in the constructor.
// In that case, the recursion would kill Delphi almost instantly.
if not FSelfChange then
begin
EditText := NewValue;
DoValueChanged;
end;
end;
procedure TJvCustomValidateEdit.SetDisplayPrefix(const NewValue: string);
begin
FDisplayPrefix := NewValue;
DisplayText;
end;
procedure TJvCustomValidateEdit.SetDisplaySuffix(const NewValue: string);
begin
FDisplaySuffix := NewValue;
DisplayText;
end;
procedure TJvCustomValidateEdit.CriticalPointsChange(Sender: TObject);
begin
SetFontColor;
Invalidate;
end;
function TJvCustomValidateEdit.IsValid: Boolean;
begin
Result := True;
case FCriticalPoints.CheckPoints of
cpMaxValue:
Result := IsLower(AsFloat, FCriticalPoints.MaxValue, FCriticalPoints.MaxValueIncluded);
cpMinValue:
Result := IsGreater(AsFloat, FCriticalPoints.MinValue, FCriticalPoints.MinValueIncluded);
cpBoth:
Result := IsLower(AsFloat, FCriticalPoints.MaxValue, FCriticalPoints.MaxValueIncluded) and
IsGreater(AsFloat, FCriticalPoints.MinValue, FCriticalPoints.MinValueIncluded);
end;
if Assigned(FOnIsValid) then
FOnIsValid(Self, Result);
end;
procedure TJvCustomValidateEdit.SetFontColor;
begin
Font.OnChange := nil;
case FCriticalPoints.CheckPoints of
cpNone:
Font.Color := FStandardFontColor;
cpMinValue:
if IsLower(AsFloat, FCriticalPoints.MinValue, not FCriticalPoints.MinValueIncluded) then
Font.Color := FCriticalPoints.ColorBelow
else
Font.Color := FStandardFontColor;
cpMaxValue:
if IsGreater(AsFloat, FCriticalPoints.MaxValue, not FCriticalPoints.MaxValueIncluded) then
Font.Color := FCriticalPoints.ColorAbove
else
Font.Color := FStandardFontColor;
cpBoth:
if IsGreater(AsFloat, FCriticalPoints.MaxValue, not FCriticalPoints.MaxValueIncluded) then
Font.Color := FCriticalPoints.ColorAbove
else
if IsLower(AsFloat, FCriticalPoints.MinValue, not FCriticalPoints.MinValueIncluded) then
Font.Color := FCriticalPoints.ColorBelow
else
Font.Color := FStandardFontColor;
end;
Font.OnChange := FontChange;
Invalidate;
end;
procedure TJvCustomValidateEdit.FontChange(Sender: TObject);
begin
FStandardFontColor := Font.Color;
if Assigned(FOldFontChange) then
FOldFontChange(Sender);
end;
procedure TJvCustomValidateEdit.EnforceMaxValue;
begin
{ Check the Value is within this range }
if FHasMaxValue and (FDisplayFormat in [dfBinary, dfCurrency, dfFloat, dfFloatGeneral,
dfHex, dfInteger, dfOctal, dfPercent, dfScientific, dfYear]) and
(AsFloat > FMaxValue) and not FEnforcingMinMaxValue then
begin
FEnforcingMinMaxValue := True;
try
SetAsFloat(FMaxValue);
finally
FEnforcingMinMaxValue := False;
end;
end;
end;
procedure TJvCustomValidateEdit.EnforceMinValue;
begin
{ Check the Value is within this range }
if FHasMinValue and (FDisplayFormat in [dfBinary, dfCurrency, dfFloat, dfFloatGeneral,
dfHex, dfInteger, dfOctal, dfPercent, dfScientific, dfYear]) and
(AsFloat < FMinValue) and not FEnforcingMinMaxValue then
begin
FEnforcingMinMaxValue := True;
try
SetAsFloat(FMinValue);
finally
FEnforcingMinMaxValue := False;
end;
end;
end;
//=== { TJvValidateEditCriticalPoints } ======================================
constructor TJvValidateEditCriticalPoints.Create;
begin
inherited Create;
SetDefaults(cpNone, clBlue, clRed);
FMaxValueIncluded := False;
FMinValueIncluded := False;
end;
procedure TJvValidateEditCriticalPoints.SetCheckPoints(NewValue: TJvValidateEditCriticalPointsCheck);
begin
if FCheckPoints <> NewValue then
begin
FCheckPoints := NewValue;
DoChanged;
end;
end;
procedure TJvValidateEditCriticalPoints.SetColorAbove(NewValue: TColor);
begin
if FColorAbove <> NewValue then
begin
FColorAbove := NewValue;
DoChanged;
end;
end;
procedure TJvValidateEditCriticalPoints.SetColorBelow(NewValue: TColor);
begin
if FColorBelow <> NewValue then
begin
FColorBelow := NewValue;
DoChanged;
end;
end;
procedure TJvValidateEditCriticalPoints.SetMaxValue(NewValue: Double);
begin
if FMaxValue <> NewValue then
begin
FMaxValue := NewValue;
DoChanged;
end;
end;
procedure TJvValidateEditCriticalPoints.SetMinValue(NewValue: Double);
begin
if FMinValue <> NewValue then
begin
FMinValue := NewValue;
DoChanged;
end;
end;
procedure TJvValidateEditCriticalPoints.DoChanged;
begin
if Assigned(FOnChange) then
FOnChange(Self);
end;
procedure TJvValidateEditCriticalPoints.Assign(Source: TPersistent);
var
LocalSource: TJvValidateEditCriticalPoints;
begin
if Source is TJvValidateEditCriticalPoints then
begin
LocalSource := TJvValidateEditCriticalPoints(Source);
CheckPoints := LocalSource.CheckPoints;
ColorAbove := LocalSource.ColorAbove;
ColorBelow := LocalSource.ColorBelow;
MaxValue := LocalSource.MaxValue;
MinValue := LocalSource.MinValue;
end
else
inherited Assign(Source);
end;
function TJvValidateEditCriticalPoints.IsCheckPointsStored: Boolean;
begin
Result := (FCheckPoints <> FDefCheckPoints);
end;
function TJvValidateEditCriticalPoints.IsColorAboveStored: Boolean;
begin
Result := (FColorAbove <> FDefColorAbove);
end;
function TJvValidateEditCriticalPoints.IsColorBelowStored: Boolean;
begin
Result := (FColorBelow <> FDefColorBelow);
end;
procedure TJvValidateEditCriticalPoints.SetDefaults(ACheckPoints: TJvValidateEditCriticalPointsCheck;
AColorAbove, AColorBelow: TColor);
begin
FDefCheckPoints := ACheckPoints;
FCheckPoints := ACheckPoints;
FDefColorAbove := AColorAbove;
FColorAbove := AColorAbove;
FDefColorBelow := AColorBelow;
FColorBelow := AColorBelow;
end;
{$IFDEF UNITVERSIONING}
initialization
RegisterUnitVersion(HInstance, UnitVersioning);
finalization
UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}
end.