mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 05:28:17 +02:00
739 lines
18 KiB
PHP
739 lines
18 KiB
PHP
{%MainUnit spinex.pp}
|
|
|
|
{
|
|
*****************************************************************************
|
|
This file is part of the Lazarus Component Library (LCL)
|
|
|
|
See the file COPYING.modifiedLGPL.txt, included in this distribution,
|
|
for details about the license.
|
|
*****************************************************************************
|
|
|
|
}
|
|
|
|
const
|
|
NvbStrings: Array[TNullValueBehaviour] of string = (
|
|
'nvbShowTextHint',
|
|
'nvbLimitedNullValue',
|
|
'nvbMinValue',
|
|
'nvbMaxValue',
|
|
'nvbInitialValue'
|
|
);
|
|
|
|
Digits = ['0'..'9'];
|
|
AllowedControlChars = [#8,#9,^C,^X,^V,^Z];
|
|
|
|
function DbgS(ANvb: TNullValueBehaviour): String;
|
|
begin
|
|
Result := NvbStrings[ANvb];
|
|
end;
|
|
|
|
{ TSpinEditEx }
|
|
|
|
|
|
procedure TSpinEditExBase.UpdateControl;
|
|
var
|
|
ANumber: T;
|
|
begin
|
|
{$ifdef debugspinex}
|
|
debugln(['TSpinEditExBase.UpdateControl: FSettingValue=',FSettingValue]);
|
|
{$endif}
|
|
//if (MaxValue < MinValue) then FMaxValue := MinValue;
|
|
if (FNullValueBehaviour <> nvbShowTextHint) then
|
|
FValue := GetLimitedValue(FValue);
|
|
if (not HandleAllocated) then Exit;
|
|
|
|
if ([csLoading, csDestroying] * ComponentState <> []) then
|
|
FUpdatePending := True
|
|
else
|
|
begin
|
|
FUpdatePending := False;
|
|
//Update the Text
|
|
if (FNullValueBehaviour = nvbShowTextHint) then
|
|
begin
|
|
if not FSettingValue then
|
|
begin
|
|
if TextIsNumber(Text, ANumber) then
|
|
Text := ValueToStr(GetLimitedValue(ANumber))
|
|
else
|
|
Text := EmptyStr;
|
|
end
|
|
else
|
|
begin
|
|
if IsOutOfLimits(FValue) then
|
|
Text := EmptyStr
|
|
else
|
|
Text := ValueToStr(FValue);
|
|
end;
|
|
end
|
|
else
|
|
Text := ValueToStr(GetLimitedValue(FValue));
|
|
end;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.UpDownChangingEx(Sender: TObject;
|
|
var AllowChange: Boolean; NewValue: SmallInt; Direction: TUpDownDirection);
|
|
begin
|
|
if ReadOnly then Exit;
|
|
Case Direction of
|
|
updUp: SpinUpDown(True);
|
|
updDown: SpinUpDown(False);
|
|
end;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.UpDownClick(Sender: TObject; {%H-}Button: TUDBtnType);
|
|
begin
|
|
BuddyClick;
|
|
end;
|
|
|
|
function TSpinEditExBase.GetBuddyClassType: TControlClass;
|
|
begin
|
|
Result := TUpDown;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.DoEnter;
|
|
begin
|
|
inherited DoEnter;
|
|
FInitialValue := GetValue;
|
|
end;
|
|
|
|
function TSpinEditExBase.RealGetText: TCaption;
|
|
begin
|
|
if HandleAllocated then
|
|
Result := inherited RealGetText
|
|
else
|
|
Result := ValueToStr(FValue);
|
|
end;
|
|
|
|
procedure TSpinEditExBase.Reset;
|
|
begin
|
|
if IsMasked then
|
|
inherited Reset
|
|
else
|
|
Value := FInitialValue;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.EditEditingDone;
|
|
begin
|
|
inherited EditEditingDone;
|
|
GetValue;
|
|
{$ifdef debugspinex}
|
|
debugln(['TSpinEditExBase.EditingDone:']);
|
|
debugln([' FValue = ',FValue,' Text = "',Text,'"']);
|
|
{$endif}
|
|
UpdateControl;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.EditKeyPress(var Key: Char);
|
|
begin
|
|
inherited EditKeyPress(Key);
|
|
if not KeyAllowed(Key) then
|
|
Key := #0;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.EditUtf8KeyPress(var UTF8Key: TUTF8Char);
|
|
begin
|
|
inherited EditUtf8KeyPress(UTF8Key);
|
|
if not Utf8KeyAllowed(Utf8Key) then
|
|
Utf8Key := '';
|
|
end;
|
|
|
|
procedure TSpinEditExBase.EditChange;
|
|
begin
|
|
{$ifdef debugspinex}
|
|
debugln('TSpinEditExBase.EditChange');
|
|
{$endif}
|
|
if FMustSetModifiedToTrueInOnChange then
|
|
begin
|
|
Modified := True;
|
|
FMustSetModifiedToTrueInOnChange := False;
|
|
{$ifdef debugspinex}
|
|
debugln('FMustSetModifiedToTrue=True, Setting Modifed to True');
|
|
{$endif}
|
|
end;
|
|
inherited EditChange;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.EditKeyDown(var Key: word; Shift: TShiftState);
|
|
begin
|
|
inherited EditKeyDown(Key, Shift);
|
|
if (Key = VK_Escape) and (Shift = []) then
|
|
begin
|
|
Key := 0;
|
|
Reset;
|
|
end
|
|
else
|
|
if FArrowKeys and (Key = VK_UP) and (Shift = []) then
|
|
begin
|
|
//disable the key also if ReadOnly = True, so it won;t suddenly start moving the caret (which it doesn't if ArroKeys = True)
|
|
Key := 0;
|
|
if not ReadOnly then
|
|
SpinUpDown(True);
|
|
end
|
|
else
|
|
if FArrowKeys and (Key = VK_Down) and (Shift = []) then
|
|
begin
|
|
Key := 0;
|
|
if not ReadOnly then
|
|
SpinUpDown(False);
|
|
end
|
|
end;
|
|
|
|
procedure TSpinEditExBase.SetMaxValue(const AValue: T);
|
|
begin
|
|
if FMaxValue = AValue then Exit;
|
|
FMaxValue := AValue;
|
|
UpdateControl;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.SetMinValue(const AValue: T);
|
|
begin
|
|
if FMinValue = AValue then Exit;
|
|
FMinValue := AValue;
|
|
UpdateControl;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.SetIncrement(const AIncrement: T);
|
|
begin
|
|
if AIncrement = FIncrement then Exit;
|
|
if AIncrement > 0 then
|
|
FIncrement := AIncrement
|
|
else
|
|
FIncrement := -AIncrement;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.InitializeWnd;
|
|
begin
|
|
inherited InitializeWnd;
|
|
// at this point Text will be empty, which in UpdateControl will invoke the NullValueBehaviour
|
|
// so make sure that Text will represent FValue now
|
|
Text := ValueToStr(FValue);
|
|
UpdateControl;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.Loaded;
|
|
begin
|
|
inherited Loaded;
|
|
UpDown.MinRepeatInterval := FMinRepeatValue;
|
|
if FUpdatePending then UpdateControl;
|
|
end;
|
|
|
|
function TSpinEditExBase.MaxValueStored: Boolean;
|
|
begin
|
|
Result := not SameValue(FMaxValue, DefMaxValue);
|
|
end;
|
|
|
|
function TSpinEditExBase.MinValueStored: Boolean;
|
|
begin
|
|
Result := not SameValue(FMinValue, DefMinValue);
|
|
end;
|
|
|
|
procedure TSpinEditExBase.EditMouseWheelUp(Shift: TShiftState;
|
|
MousePos: TPoint; var Handled: Boolean);
|
|
begin
|
|
inherited EditMouseWheelUp(Shift, MousePos, Handled);
|
|
if not Handled then
|
|
SpinUpDown(True);
|
|
end;
|
|
|
|
procedure TSpinEditExBase.EditMouseWheelDown(Shift: TShiftState;
|
|
MousePos: TPoint; var Handled: Boolean);
|
|
begin
|
|
inherited EditMouseWheelDown(Shift, MousePos, Handled);
|
|
if not Handled then
|
|
SpinUpDown(False);
|
|
end;
|
|
|
|
procedure TSpinEditExBase.SetValue(const AValue: T);
|
|
var
|
|
ValueFromText: T;
|
|
begin
|
|
{$ifdef debugspinex}
|
|
debugln(['TSpinEditExBase.SetValue: AValue = ',AValue, ' , FValue=',FValue,' Text="',Text,'"']);
|
|
{$endif}
|
|
if (FValue = AValue)
|
|
//if you set text by code (or paste it) and text is not a valid float, then FValue will hold the previous value
|
|
//and in that case we should not exit here...
|
|
and ({TryStrToFloat(Text, ValueFromText, FFS)} TextIsNumber(Text, ValueFromText) and (ValueFromText = FValue)) then Exit;
|
|
FSettingValue := True;
|
|
FValue := AValue;
|
|
|
|
FUpdatePending := True;
|
|
UpdateControl;
|
|
if not (csLoading in ComponentState) then
|
|
FSettingValue := False;
|
|
end;
|
|
|
|
function TSpinEditExBase.GetValue: T;
|
|
begin
|
|
if HandleAllocated
|
|
and not (wcfCreatingHandle in FWinControlFlags) then
|
|
begin
|
|
FValue := StrToValue(Text);
|
|
end;
|
|
Result := FValue;
|
|
end;
|
|
|
|
function TSpinEditExBase.IncrementStored: Boolean;
|
|
begin
|
|
Result := not SameValue(FIncrement, DefIncrement);
|
|
end;
|
|
|
|
function TSpinEditExBase.IsLimited: Boolean;
|
|
begin
|
|
Result := MaxValue > MinValue;
|
|
end;
|
|
|
|
function TSpinEditExBase.IsOutOfLimits(AValue: T): Boolean;
|
|
begin
|
|
Result := IsLimited and ((AValue < MinValue) or (AValue > MaxValue));
|
|
end;
|
|
|
|
function TSpinEditExBase.GetEdit: TGEEdit;
|
|
begin
|
|
Result := BaseEditor;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.SetMinRepeatValue(AValue: Byte);
|
|
begin
|
|
if FMinRepeatValue = AValue then Exit;
|
|
FMinRepeatValue := AValue;
|
|
if not (csLoading in ComponentState) then
|
|
UpDown.MinRepeatInterval := FMinRepeatValue;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.SpinUpDown(Up: Boolean);
|
|
var
|
|
OldValue, NewValue: T;
|
|
begin
|
|
if not TextIsNumber(Text, OldValue) then
|
|
NewValue := MinValue
|
|
else
|
|
begin
|
|
if IsOutOfLimits(OldValue) then
|
|
NewValue := GetLimitedValue(OldValue)
|
|
else
|
|
begin
|
|
if Up then
|
|
NewValue := GetLimitedValue(SafeInc(OldValue))
|
|
else
|
|
NewValue := GetLimitedValue(SafeDec(OldValue));
|
|
end;
|
|
end;
|
|
FMustSetModifiedToTrueInOnChange := True;
|
|
SetValue(NewValue);
|
|
//RealSetText of the Edit will set it to False
|
|
Modified := True;
|
|
end;
|
|
|
|
function TSpinEditExBase.GetNullValue: T;
|
|
begin
|
|
Result := FNullValue;
|
|
end;
|
|
|
|
function TSpinEditExBase.GetOrientation: TUDOrientation;
|
|
begin
|
|
Result := UpDown.Orientation;
|
|
end;
|
|
|
|
function TSpinEditExBase.GetUpDown: TUpDown;
|
|
begin
|
|
Result := TUpDown(Buddy);
|
|
end;
|
|
|
|
procedure TSpinEditExBase.SetNullValue(AValue: T);
|
|
begin
|
|
if (FNullValue = AValue) then Exit;
|
|
FNullValue := AValue;
|
|
UpdateControl;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.SetOrientation(AValue: TUDOrientation);
|
|
var
|
|
w: Integer;
|
|
begin
|
|
if GetOrientation = AValue then
|
|
exit;
|
|
w := UpDown.Width;
|
|
UpDown.Orientation := AValue;
|
|
case GetOrientation of
|
|
udVertical: UpDown.Width := w div 2;
|
|
udHorizontal: UpDown.Width := w * 2;
|
|
end;
|
|
end;
|
|
|
|
constructor TSpinEditExBase.Create(TheOwner: TComponent);
|
|
begin
|
|
inherited Create(TheOwner);
|
|
FMustSetModifiedToTrueInOnChange := False;
|
|
FArrowKeys := True;
|
|
FIncrement := DefIncrement;
|
|
FMaxValue := DefMaxValue;
|
|
FMinValue := DefMinValue;
|
|
FUpdatePending := True;
|
|
FSettingValue := False;
|
|
FNullValueBehaviour := nvbMinValue;
|
|
FMinRepeatValue := DefMinRepeatValue;
|
|
|
|
UpDown.ControlStyle := UpDown.ControlStyle + [csNoDesignVisible];
|
|
Edit.Alignment := taRightJustify;
|
|
|
|
{
|
|
A note regarding the Updown control.
|
|
It is by design that UpDown is not set to associate with the Edit.
|
|
Amongst others, it would make it impossible to use with floats,
|
|
nor have a NullValue.
|
|
It also does align as it should when associated.
|
|
}
|
|
UpDown.OnChangingEx := @UpDownChangingEx;
|
|
//OnCick signature of TUpDown differs from TControl.OnClick,
|
|
//Yhe assigning of OnClick in inherited constructor
|
|
//sets TControl(Buddy).OnClick to fire BuddyClick, and that won't do
|
|
//since TUpDown does not fire a regular TControl.OnClick event
|
|
UpDown.OnClick := @UpDownClick;
|
|
|
|
with GetControlClassDefaultSize do
|
|
SetInitialBounds(0, 0, CX, CY);
|
|
end;
|
|
|
|
function TSpinEditExBase.GetLimitedValue(const AValue: T): T;
|
|
begin
|
|
Result := AValue;
|
|
//Delphi does not constrain when MinValue = MaxValue, and does if MaxValue < MinValue,
|
|
//but the latter makes absolutely no sense at all.
|
|
if FMaxValue > FMinValue then
|
|
begin
|
|
if Result < FMinValue then Result := FMinValue;
|
|
if Result > FMaxValue then Result := FMaxValue;
|
|
end;
|
|
end;
|
|
|
|
function TSpinEditExBase.StrToValue(const S: String): T;
|
|
var
|
|
Def, N: T;
|
|
begin
|
|
{$ifdef debugspinex}
|
|
debugln(['TSpinEditExBase.StrToValue: S="',S,'"']);
|
|
{$endif}
|
|
case FNullValueBehaviour of
|
|
nvbShowTextHint: Def := FNullValue;
|
|
nvbLimitedNullValue: Def := GetLimitedValue(FNullValue);
|
|
nvbMinValue: Def := FMinValue;
|
|
nvbMaxValue: Def := MaxValue;
|
|
nvbInitialValue: Def := FInitialValue;
|
|
end;
|
|
try
|
|
if TextIsNumber(S, N) then
|
|
Result := N
|
|
else
|
|
Result := Def;
|
|
if not (FNullValueBehaviour = nvbShowTextHint) then
|
|
Result := GetLimitedValue(Result);
|
|
except
|
|
Result := Def;
|
|
end;
|
|
{$ifdef debugspinex}
|
|
debugln([' Result=',(Result)]);
|
|
{$endif}
|
|
end;
|
|
|
|
function TSpinEditExBase.KeyAllowed(Key: Char): Boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
function TSpinEditExBase.Utf8KeyAllowed(Key: TUTF8Char): Boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
procedure TSpinEditExBase.FinalizeWnd;
|
|
begin
|
|
GetValue;
|
|
inherited FinalizeWnd;
|
|
end;
|
|
|
|
|
|
{ TCustomFloatSpinEditEx }
|
|
|
|
function TCustomFloatSpinEditEx.GetDecimalSeparator: Char;
|
|
begin
|
|
Result := FFS.DecimalSeparator;
|
|
end;
|
|
|
|
procedure TCustomFloatSpinEditEx.SetDecimalSeparator(AValue: Char);
|
|
begin
|
|
if (AValue = FFS.DecimalSeparator) then Exit;
|
|
FFS.DecimalSeparator := AValue;
|
|
UpdateControl;
|
|
end;
|
|
|
|
procedure TCustomFloatSpinEditEx.SetDisplayMode(AValue: TDisplayMode);
|
|
begin
|
|
if FDisplayMode = AValue then Exit;
|
|
GetValue;
|
|
FDisplayMode := AValue;
|
|
UpdateControl;
|
|
end;
|
|
|
|
procedure TCustomFloatSpinEditEx.SetExponentDigits(AValue: Integer);
|
|
begin
|
|
if FExponentDigits = AValue then Exit;
|
|
GetValue;
|
|
FExponentDigits := AValue;
|
|
UpdateControl;
|
|
end;
|
|
|
|
procedure TCustomFloatSpinEditEx.SetExponentialFormatLimitNeg(AValue: Integer);
|
|
begin
|
|
if (FExponentialFormatLimitNeg = AValue) or (AValue > -1) then Exit;
|
|
GetValue;
|
|
FExponentialFormatLimitNeg := AValue;
|
|
UpdateControl;
|
|
end;
|
|
|
|
procedure TCustomFloatSpinEditEx.SetExponentialFormatLimitPos(AValue: Integer);
|
|
begin
|
|
if (FExponentialFormatLimitPos = AValue) or (AValue < 0) then Exit;
|
|
GetValue;
|
|
FExponentialFormatLimitPos := AValue;
|
|
UpdateControl;
|
|
end;
|
|
|
|
procedure TCustomFloatSpinEditEx.SetPrecision(AValue: Integer);
|
|
begin
|
|
if FPrecision = AValue then Exit;
|
|
GetValue;
|
|
FPrecision := AValue;
|
|
UpdateControl;
|
|
end;
|
|
|
|
function TCustomFloatSpinEditEx.GetFormatsettings: TFormatSettings;
|
|
begin
|
|
Result := FFS;
|
|
end;
|
|
|
|
procedure TCustomFloatSpinEditEx.EditKeyPress(var Key: char);
|
|
begin
|
|
if (Key in ['.',',']) then Key := FFS.Decimalseparator;
|
|
inherited EditKeyPress(Key);
|
|
end;
|
|
|
|
function TCustomFloatSpinEditEx.TextIsNumber(const S: String; out ANumber: Double
|
|
): Boolean;
|
|
begin
|
|
{$ifdef debugspinex}
|
|
DbgOut(['TextIsNumber, S ="',S,'": Result = ']);
|
|
{$endif}
|
|
try
|
|
Result := TryStrToFloat(S, ANumber, FFS);
|
|
except
|
|
Result := False;
|
|
end;
|
|
{$ifdef debugspinex}
|
|
debugln([Result]);
|
|
{$endif}
|
|
end;
|
|
|
|
function TCustomFloatSpinEditEx.SafeInc(AValue: Double): Double;
|
|
begin
|
|
if ((AValue > 0) and (AValue > (MaxDouble-FIncrement))) then
|
|
Result := MaxDouble
|
|
else
|
|
Result := AValue + FIncrement;
|
|
end;
|
|
|
|
function TCustomFloatSpinEditEx.SafeDec(AValue: Double): Double;
|
|
begin
|
|
if (AValue < 0) and ((-MaxDouble + FIncrement) > AValue) then
|
|
Result := -MaxDouble
|
|
else
|
|
Result := AValue - FIncrement;
|
|
end;
|
|
|
|
function TCustomFloatSpinEditEx.SameValue(AValue1, AValue2: Double): Boolean;
|
|
begin
|
|
Result := Math.SameValue(AValue1, AValue2);
|
|
end;
|
|
|
|
procedure TCustomFloatSpinEditEx.SetDecimals(ADecimals: Integer);
|
|
begin
|
|
if (FDecimals = ADecimals) or (ADecimals < 0) then Exit;
|
|
//if we increment DecimalPlaces first set FValue to correctly have the current DecimalPlaces decimals,
|
|
//and only then do the increment (GetValue will update FValue to have FDecimalPlaces decimals)
|
|
//Issue #0034370
|
|
if (ADecimals > FDecimals) then GetValue;
|
|
FDecimals := ADecimals;
|
|
UpdateControl;
|
|
end;
|
|
|
|
class procedure TCustomFloatSpinEditEx.WSRegisterClass;
|
|
begin
|
|
inherited WSRegisterClass;
|
|
RegisterPropertyToSkip(TCustomFloatSpinEditEx, 'NumbersOnly', 'Should never have been published for this control.', '');
|
|
end;
|
|
|
|
|
|
function TCustomFloatSpinEditEx.ValueToStr(const AValue: Double): String;
|
|
var
|
|
LValue: Double;
|
|
begin
|
|
LValue := GetLimitedValue(AValue);
|
|
case FDisplayMode of
|
|
dmFixed:
|
|
begin
|
|
Result := FloatToStrF(LValue, ffFixed, 20, DecimalPlaces, FFS)
|
|
end;
|
|
dmScientific:
|
|
begin
|
|
Result := FloatToStrF(LValue, ffExponent, FPrecision, FExponentDigits, FFS)
|
|
end;
|
|
dmAuto,dmAutoZeroFixed:
|
|
begin
|
|
//it is safe to use (LValue=0.0) and not SameValue, since this is about text input
|
|
//and '0.0000' as text will be converted to a bitpattern of all zero's
|
|
if not ((FDisplayMode = dmAutoZeroFixed) and (LValue = 0.0)) and
|
|
(
|
|
(Abs(LValue) > Power(10,FExponentialFormatLimitPos)) or
|
|
(Abs(LValue) < Power(10,FExponentialFormatLimitNeg))
|
|
) then
|
|
Result := FloatToStrF(LValue, ffExponent, FPrecision, FExponentDigits, FFS)
|
|
else
|
|
Result := FloatToStrF(LValue, ffFixed, 20, DecimalPlaces, FFS)
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomFloatSpinEditEx.KeyAllowed(Key: Char): Boolean;
|
|
begin
|
|
{
|
|
Disallow any key that is not a digit, decimalseparator or '+', '-' or 'E/e'
|
|
For ease of use translate any decimalpoint or comma to DecimalSeparator
|
|
Tab, BackSpace, Cut, Paste, Copy, Undo of course should be passed onto inherited KeyPress
|
|
If FDecimals = 0, disallow decimalseparator also
|
|
}
|
|
Result := (Key in (Digits + AllowedControlChars + [FFS.DecimalSeparator,'-','+','e','E']));
|
|
if Result and (Key = FFS.DecimalSeparator) and (FDecimals = 0) then
|
|
Result := False;
|
|
end;
|
|
|
|
|
|
constructor TCustomFloatSpinEditEx.Create(TheOwner: TComponent);
|
|
begin
|
|
inherited Create(TheOwner);
|
|
FFS := DefaultFormatSettings;
|
|
FFS.DecimalSeparator := DefDecimalSeparator;
|
|
FDecimals := DefDecimals;
|
|
FExponentialFormatLimitPos := 6;
|
|
FExponentialFormatLimitNeg := -6;
|
|
FPrecision := 6;
|
|
FExponentDigits := 2;
|
|
FDisplayMode := dmFixed;
|
|
end;
|
|
|
|
|
|
|
|
{ TCustomSpinEditEx }
|
|
|
|
function InsertThousandSeparator(const ValueS, AThousandSep: String): String;
|
|
// A bit more complicated, but 3 times faster than repeated Insert() calls
|
|
var
|
|
ResLen, ResPos, SLen, i, j: Integer;
|
|
begin
|
|
if (AThousandSep = '') then
|
|
Exit(ValueS);
|
|
Result := '';
|
|
SLen := Length(ValueS);
|
|
//Needed separators = ((SLen - 1) div 3)
|
|
ResLen := SLen + ((SLen - 1) div 3) * Length(AThousandSep);
|
|
SetLength(Result, ResLen);
|
|
ResPos := ResLen;
|
|
for i := Length(ValueS) downto 1 do
|
|
begin
|
|
if (SLen <> i) and ((SLen-i) mod 3 = 0) then
|
|
begin
|
|
for j := Length(AThousandSep) downto 1 do
|
|
begin
|
|
Result[ResPos] := AThousandSep[j];
|
|
Dec(ResPos);
|
|
end;
|
|
end;
|
|
Result[ResPos] := ValueS[i];
|
|
Dec(ResPos);
|
|
end;
|
|
end;
|
|
|
|
function RemoveThousandSeparator(const ValueS: String; AThousandSeparator: String): String;
|
|
begin
|
|
if (AThousandSeparator = '') then
|
|
Result := ValueS
|
|
else
|
|
Result := StringReplace(ValueS, AThousandSeparator, '', [rfReplaceAll]);
|
|
end;
|
|
|
|
procedure TCustomSpinEditEx.SetThousandSeparator(AValue: String);
|
|
begin
|
|
if FThousandSeparator = AValue then Exit;
|
|
FThousandSeparator := AValue;
|
|
UpdateControl;
|
|
end;
|
|
|
|
|
|
function TCustomSpinEditEx.SafeInc(AValue: Int64): Int64;
|
|
begin
|
|
if ((AValue > 0) and (AValue > (High(Int64)-FIncrement))) then
|
|
Result := High(Int64)
|
|
else
|
|
Result := AValue + FIncrement;
|
|
end;
|
|
|
|
function TCustomSpinEditEx.SafeDec(AValue: Int64): Int64;
|
|
begin
|
|
if (AValue < 0) and ((Low(Int64) + FIncrement) > AValue) then
|
|
Result := Low(Int64)
|
|
else
|
|
Result := AValue - FIncrement;
|
|
end;
|
|
|
|
function TCustomSpinEditEx.SameValue(AValue1, AValue2: Int64): Boolean;
|
|
begin
|
|
Result := (AValue1 = AValue2);
|
|
end;
|
|
|
|
function TCustomSpinEditEx.TextIsNumber(const S: String; out ANumber: Int64
|
|
): Boolean;
|
|
var
|
|
N: Int64;
|
|
begin
|
|
{$ifdef debugspinex}
|
|
DbgOut(['TCustomSpinEditEx.TextIsNumber: S = "',S,'" Result = ']);
|
|
{$endif}
|
|
|
|
try
|
|
Result := TryStrToInt64(RemoveThousandSeparator(S, FThousandSeparator), N);
|
|
ANumber := N;
|
|
except
|
|
Result := False;
|
|
end;
|
|
{$ifdef debugspinex}
|
|
debugln([Result]);
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
function TCustomSpinEditEx.ValueToStr(const AValue: Int64): String;
|
|
begin
|
|
Result := IntToStr(AValue);
|
|
if (FThousandSeparator <> '') then
|
|
Result := InsertThousandSeparator(Result, FThousandSeparator);
|
|
end;
|
|
|
|
function TCustomSpinEditEx.KeyAllowed(Key: Char): Boolean;
|
|
begin
|
|
{Disallow any key that is not a digit or - or (part of) FThousandSeparator
|
|
Tab, BackSpace, Cut, Paste, Copy, Undo of course should be passed onto inherited KeyPress
|
|
}
|
|
Result := ((Key in (Digits + AllowedControlChars + ['-'])) or (Pos(Key, FThousandSeparator) > 0));
|
|
if Result and (Key = '-') and IsLimited and (MinValue >= 0) then
|
|
Result := False;
|
|
end;
|