Componentes.Terceros.DevExp.../internal/x.46/2/ExpressEditors Library 5/Sources/cxCalc.pas

1921 lines
59 KiB
ObjectPascal

{********************************************************************}
{ }
{ Developer Express Visual Component Library }
{ ExpressEditors }
{ }
{ Copyright (c) 1998-2009 Developer Express Inc. }
{ ALL RIGHTS RESERVED }
{ }
{ The entire contents of this file is protected by U.S. and }
{ International Copyright Laws. Unauthorized reproduction, }
{ reverse-engineering, and distribution of all or any portion of }
{ the code contained in this file is strictly prohibited and may }
{ result in severe civil and criminal penalties and will be }
{ prosecuted to the maximum extent possible under the law. }
{ }
{ RESTRICTIONS }
{ }
{ THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES }
{ (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE }
{ SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS }
{ LICENSED TO DISTRIBUTE THE EXPRESSEDITORS AND ALL }
{ ACCOMPANYING VCL CONTROLS AS PART OF AN EXECUTABLE PROGRAM ONLY. }
{ }
{ THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED }
{ FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE }
{ COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE }
{ AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT }
{ AND PERMISSION FROM DEVELOPER EXPRESS INC. }
{ }
{ CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON }
{ ADDITIONAL RESTRICTIONS. }
{ }
{********************************************************************}
unit cxCalc;
{$I cxVer.inc}
interface
uses
Windows, Messages,
SysUtils, Classes, Graphics, Controls, Forms, StdCtrls, Clipbrd,
cxClasses, cxControls, cxContainer, cxGraphics, cxDataStorage, cxDataUtils,
cxButtons, cxEdit, cxDropDownEdit, cxEditConsts, cxFormats, cxLookAndFeelPainters,
cxTextEdit, cxFilterControlUtils;
const
cxMaxCalcPrecision = cxEditDefaultPrecision;
cxDefCalcPrecision = cxMaxCalcPrecision;
// Size
cxMinCalcFontSize = 8;
cxCalcMinBoldFontSize = 10;
cxMinCalcBtnWidth = 28;
cxMinCalcBtnHeight = 22;
cxMinCalcLargeBtnWidth = Integer(Trunc(1.7*cxMinCalcBtnWidth));
cxMinCalcXOfs = 3;
cxMinCalcYOfs = 3;
cxMinCalcWidth = (cxMinCalcXOfs+cxMinCalcBtnWidth)*6+cxMinCalcXOfs*3+3;
cxMinCalcHeight = (cxMinCalcYOfs+cxMinCalcBtnHeight)*5+cxMinCalcYOfs+3;
type
TcxCalcState = (csFirst, csValid, csError);
const
BtnCaptions : array [cbBack..cbEqual] of string = ('Back', 'CE', 'C',
'MC', 'MR', 'MS', 'M+',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'+/-', ',', '/', '*', '-', '+', 'sqrt', '%', '1/x', '=');
type
TcxButtonInfo = record
BtnRect: TRect;
Down: Boolean;
Grayed: Boolean;
Kind: TcxCalcButtonKind;
Text: string;
end;
TCalcButtons = array [TcxCalcButtonKind] of TcxButtonInfo;
{ TcxCustomCalculator }
TcxCalcButtonClick = procedure(Sender: TObject; var ButtonKind : TcxCalcButtonKind) of object;
TcxCalcGetEditValue = procedure(Sender: TObject; var Value : String) of object;
TcxCalcSetEditValue = procedure(Sender: TObject; const Value : String) of object;
TcxCustomCalculator = class(TcxControl)
private
{calc style}
FAutoFontSize : Boolean;
FBeepOnError: Boolean;
FBorderStyle : TBorderStyle;
FFocusRectVisible : Boolean;
{calc size}
FCalcFontSize : Integer;
FCalcBtnWidth : Integer;
FCalcBtnHeight : Integer;
FCalcLargeBtnWidth : Integer;
FCalcXOfs : Integer;
FCalcYOfs : Integer;
FCalcWidth : Integer;
FCalcHeight : Integer;
{math}
FMemory : Extended;
FOperator: TcxCalcButtonKind;
FOperand: Extended;
FPrecision: Byte;
FStatus: TcxCalcState;
{control}
FButtons : TCalcButtons;
FActiveButton : TcxCalcButtonKind;
FDownButton : TcxCalcButtonKind;
FPressedButton : TcxCalcButtonKind;
FTracking: Boolean;
// events
FOnDisplayChange: TNotifyEvent;
FOnButtonClick: TcxCalcButtonClick;
FOnResult: TNotifyEvent;
FOnHidePopup: TcxEditClosePopupEvent;
function GetDisplay: Extended;
procedure SetDisplay(Value: Extended);
function GetMemory: Extended;
procedure SetAutoFontSize(Value : Boolean);
procedure SetBorderStyle(Value: TBorderStyle);
procedure SetFocusRectVisible(Value : Boolean);
procedure StopTracking;
procedure TrackButton(X,Y: Integer);
procedure InvalidateButton(ButtonKind : TcxCalcButtonKind);
procedure DoButtonDown(ButtonKind : TcxCalcButtonKind);
procedure DoButtonUp(ButtonKind : TcxCalcButtonKind);
procedure Error;
procedure CheckFirst;
procedure Clear;
procedure CalcSize(AWidth, AHeight : Integer);
procedure UpdateMemoryButtons;
procedure InvalidateMemoryButtons;
procedure ResetOperands;
protected
IsPopupControl : Boolean;
function GetPainter: TcxCustomLookAndFeelPainterClass; virtual;
procedure Paint; override;
procedure CreateParams(var Params: TCreateParams); override;
procedure FontChanged; override;
procedure FocusChanged; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure MouseLeave(AControl: TControl); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure KeyPress(var Key: Char); override;
procedure KeyUp(var Key: Word; Shift: TShiftState); override;
procedure Resize; override;
procedure SetEnabled( Value: Boolean); override;
procedure CreateLayout;
procedure ButtonClick(ButtonKind : TcxCalcButtonKind);
// for link with EditControl
function GetEditorValue: String; virtual;
procedure SetEditorValue(const Value: String); virtual;
procedure HidePopup(Sender: TcxControl; AReason: TcxEditCloseUpReason); virtual;
procedure LockChanges(ALock: Boolean; AInvokeChangedOnUnlock: Boolean = True); virtual;
property Color default clBtnFace;
property ParentColor default False;
property BorderStyle: TBorderStyle read FBorderStyle write SetBorderStyle
default bsNone;
property AutoFontSize : Boolean read FAutoFontSize write SetAutoFontSize
default True;
property BeepOnError: Boolean read FBeepOnError write FBeepOnError
default True;
property ShowFocusRect : Boolean
read FFocusRectVisible write SetFocusRectVisible default True;
property Precision: Byte read FPrecision write FPrecision
default cxDefCalcPrecision;
property EditorValue : string read GetEditorValue write SetEditorValue;
property OnHidePopup: TcxEditClosePopupEvent read FOnHidePopup write FOnHidePopup;
property OnDisplayChange: TNotifyEvent
read FOnDisplayChange write FOnDisplayChange;
property OnButtonClick: TcxCalcButtonClick
read FOnButtonClick write FOnButtonClick;
property OnResult: TNotifyEvent read FOnResult write FOnResult;
public
constructor Create(AOwner: TComponent); override;
function GetButtonKindAt(X, Y : Integer) : TcxCalcButtonKind;
function GetButtonKindChar(Ch : Char) : TcxCalcbuttonKind;
function GetButtonKindKey(Key: Word; Shift: TShiftState) : TcxCalcbuttonKind;
procedure CopyToClipboard;
procedure PasteFromClipboard;
property Memory: Extended read GetMemory;
property Value: Extended read GetDisplay write SetDisplay;
published
property TabStop default True;
end;
{ TcxPopupCalculator }
TcxCustomCalcEdit = class;
TcxPopupCalculator = class(TcxCustomCalculator)
private
FEdit: TcxCustomCalcEdit;
protected
function GetEditorValue: string; override;
function GetPainter: TcxCustomLookAndFeelPainterClass; override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure KeyPress(var Key: Char); override;
procedure SetEditorValue(const Value: string); override;
procedure LockChanges(ALock: Boolean; AInvokeChangedOnUnlock: Boolean = True); override;
property Edit: TcxCustomCalcEdit read FEdit write FEdit;
public
constructor Create(AOwner: TComponent); override;
procedure Init; virtual;
end;
{ TcxCalcEditPropertiesValues }
TcxCalcEditPropertiesValues = class(TcxTextEditPropertiesValues)
private
FPrecision: Boolean;
procedure SetPrecision(Value: Boolean);
public
procedure Assign(Source: TPersistent); override;
procedure RestoreDefaults; override;
published
property Precision: Boolean read FPrecision write SetPrecision stored False;
end;
{ TcxCustomCalcEditProperties }
TcxCustomCalcEditProperties = class(TcxCustomPopupEditProperties)
private
FBeepOnError: Boolean;
FPrecision: Byte;
FQuickClose: Boolean;
FScientificFormat: Boolean;
FUseThousandSeparator: Boolean;
function GetAssignedValues: TcxCalcEditPropertiesValues;
function GetPrecision: Byte;
function IsPrecisionStored: Boolean;
procedure SetAssignedValues(Value: TcxCalcEditPropertiesValues);
procedure SetBeepOnError(Value: Boolean);
procedure SetPrecision(Value: Byte);
procedure SetQuickClose(Value: Boolean);
procedure SetScientificFormat(Value: Boolean);
procedure SetUseThousandSeparator(Value: Boolean);
protected
function GetAlwaysPostEditValue: Boolean; override;
class function GetAssignedValuesClass: TcxCustomEditPropertiesValuesClass; override;
function HasDigitGrouping(AIsDisplayValueSynchronizing: Boolean): Boolean; override;
function PopupWindowAcceptsAnySize: Boolean; override;
function StrToFloatEx(S: string; var Value: Extended): Boolean;
property AssignedValues: TcxCalcEditPropertiesValues read GetAssignedValues
write SetAssignedValues;
public
constructor Create(AOwner: TPersistent); override;
procedure Assign(Source: TPersistent); override;
class function GetContainerClass: TcxContainerClass; override;
function GetEditValueSource(AEditFocused: Boolean): TcxDataEditValueSource; override;
function IsDisplayValueValid(var DisplayValue: TcxEditValue;
AEditFocused: Boolean): Boolean; override;
function IsEditValueValid(var EditValue: TcxEditValue;
AEditFocused: Boolean): Boolean; override;
procedure PrepareDisplayValue(const AEditValue: TcxEditValue;
var DisplayValue: TcxEditValue; AEditFocused: Boolean); override;
procedure ValidateDisplayValue(var ADisplayValue: TcxEditValue;
var AErrorText: TCaption; var Error: Boolean; AEdit: TcxCustomEdit); override;
// !!!
property BeepOnError: Boolean read FBeepOnError write SetBeepOnError
default True;
property ImmediateDropDown default False;
property Precision: Byte read GetPrecision write SetPrecision
stored IsPrecisionStored;
property QuickClose: Boolean read FQuickClose write SetQuickClose
default False;
property ScientificFormat: Boolean read FScientificFormat
write SetScientificFormat default False;
property UseThousandSeparator: Boolean read FUseThousandSeparator
write SetUseThousandSeparator default False;
end;
{ TcxCalcEditProperties }
TcxCalcEditProperties = class(TcxCustomCalcEditProperties)
published
property Alignment;
property AssignedValues;
property AutoSelect;
property BeepOnError;
property ButtonGlyph;
property ClearKey;
property DisplayFormat;
property ImeMode;
property ImeName;
property ImmediatePost;
property Precision;
property ReadOnly;
property QuickClose;
property ScientificFormat;
property UseLeftAlignmentOnEditing;
property UseThousandSeparator;
property ValidateOnEnter;
property OnChange;
property OnCloseUp;
property OnEditValueChanged;
property OnInitPopup;
property OnPopup;
property OnValidate;
end;
{ TcxCustomCalcEdit }
TcxCustomCalcEdit = class(TcxCustomPopupEdit)
private
FCalculator: TcxPopupCalculator;
function GetProperties: TcxCustomCalcEditProperties;
function GetActiveProperties: TcxCustomCalcEditProperties;
function GetValue: Extended;
procedure SetProperties(Value: TcxCustomCalcEditProperties);
procedure SetValue(const Value: Extended);
protected
// IcxFormatControllerListener
procedure FormatChanged; override;
function CanDropDown: Boolean; override;
procedure CreatePopupWindow; override;
procedure DoEnter; override;
procedure DoExit; override;
procedure DoInitPopup; override;
procedure Initialize; override;
procedure InitializePopupWindow; override;
function InternalGetEditingValue: TcxEditValue; override;
function IsValidChar(Key: Char): Boolean; override;
procedure KeyPress(var Key: Char); override;
procedure PopupWindowClosed(Sender: TObject); override;
procedure PopupWindowShowed(Sender: TObject); override;
procedure PropertiesChanged(Sender: TObject); override;
function InternalPrepareEditValue(const ADisplayValue: string;
out EditValue: TcxEditValue): Boolean;
property Calculator: TcxPopupCalculator read FCalculator;
public
destructor Destroy; override;
class function GetPropertiesClass: TcxCustomEditPropertiesClass; override;
procedure PasteFromClipboard; override;
procedure PrepareEditValue(const ADisplayValue: TcxEditValue;
out EditValue: TcxEditValue; AEditFocused: Boolean); override;
property ActiveProperties: TcxCustomCalcEditProperties
read GetActiveProperties;
property Properties: TcxCustomCalcEditProperties read GetProperties
write SetProperties;
property Value: Extended read GetValue write SetValue stored False;
end;
{ TcxCalcEdit }
TcxCalcEdit = class(TcxCustomCalcEdit)
private
function GetActiveProperties: TcxCalcEditProperties;
function GetProperties: TcxCalcEditProperties;
procedure SetProperties(Value: TcxCalcEditProperties);
public
class function GetPropertiesClass: TcxCustomEditPropertiesClass; override;
property ActiveProperties: TcxCalcEditProperties read GetActiveProperties;
published
property Anchors;
property AutoSize;
property BeepOnEnter;
property Constraints;
property DragCursor;
property DragKind;
property DragMode;
property EditValue;
property Enabled;
property ImeMode;
property ImeName;
property ParentColor;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property Properties: TcxCalcEditProperties read GetProperties
write SetProperties;
property ShowHint;
property Style;
property StyleDisabled;
property StyleFocused;
property StyleHot;
property TabOrder;
property TabStop default True;
{$IFDEF DELPHI12}
property TextHint;
{$ENDIF}
property Value;
property Visible;
property OnClick;
{$IFDEF DELPHI5}
property OnContextPopup;
{$ENDIF}
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEditing;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDrag;
property BiDiMode;
property ParentBiDiMode;
property OnEndDock;
property OnStartDock;
end;
{ TcxFilterCalcEditHelper }
TcxFilterCalcEditHelper = class(TcxFilterDropDownEditHelper)
public
class function GetFilterEditClass: TcxCustomEditClass; override;
class function GetSupportedFilterOperators(
AProperties: TcxCustomEditProperties;
AValueTypeClass: TcxValueTypeClass;
AExtendedSet: Boolean = False): TcxFilterControlOperators; override;
class procedure InitializeProperties(AProperties,
AEditProperties: TcxCustomEditProperties; AHasButtons: Boolean); override;
end;
implementation
uses
{$IFDEF DELPHI6}
Variants,
{$ENDIF}
Math, cxLookAndFeels, cxVariants, dxThemeManager, dxUxTheme, dxCore;
const
ResultButtons = [cbEqual, cbPercent];
RepeatButtons = [cbBack];
OperationButtons = [cbAdd, cbSub, cbMul, cbDiv];
BorderWidth = 4;
{TcxCustomCalculator}
constructor TcxCustomCalculator.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
{init size variables}
FCalcFontSize := cxMinCalcFontSize;
FCalcBtnWidth := cxMinCalcBtnWidth;
FCalcBtnHeight := cxMinCalcBtnHeight;
FCalcLargeBtnWidth := cxMinCalcLargeBtnWidth;
FCalcXOfs := cxMinCalcXOfs;
FCalcYOfs := cxMinCalcYOfs;
FCalcWidth := cxMinCalcWidth;
FCalcHeight := cxMinCalcHeight;
{default size}
Width := FCalcWidth;
Height := FCalcHeight;
{style}
ControlStyle := [csCaptureMouse, csOpaque];
Color := clBtnFace;
ParentColor := False;
TabStop := True;
FAutoFontSize := True;
FBorderStyle := bsNone;
FBeepOnError := True;
FDownButton := cbNone;
FActiveButton := cbNone;
FPressedButton := cbNone;
FFocusRectVisible := True;
FOperator := cbEqual;
FPrecision := cxDefCalcPrecision;
Keys := [kAll, kArrows, kChars, kTab];
CreateLayout;
end;
function TcxCustomCalculator.GetButtonKindAt(X, Y : Integer) : TcxCalcButtonKind;
var i : TcxCalcButtonKind;
begin
Result := cbNone;
for i := cbBack to cbEqual do
if PtInRect(FButtons[i].BtnRect, Point(X, Y)) then
begin
Result := i;
Exit;
end;
end;
function TcxCustomCalculator.GetPainter: TcxCustomLookAndFeelPainterClass;
begin
Result := TcxStandardLookAndFeelPainter;
end;
procedure TcxCustomCalculator.Paint;
function CalculateButtonState(const AButton: TcxButtonInfo): TcxButtonState;
begin
if AButton.Grayed or not Enabled then
Result := cxbsDisabled
else
if AButton.Down then
Result := cxbsPressed
else
if (FActiveButton = AButton.Kind) and (FDownButton <> AButton.Kind) then
Result := cxbsHot
else
if IsFocused and (AButton.Kind = cbEqual) then
Result := cxbsDefault
else
Result := cxbsNormal;
end;
var
AState: TcxButtonState;
I: TcxCalcButtonKind;
begin
if not HandleAllocated then Exit;
with Canvas do
begin
{Fill Background}
Brush.Color := Self.Color;
if IsPopupControl then
GetPainter.DrawWindowContent(Self.Canvas, ClientRect)
else
GetPainter.DrawWindowContent(Self.Canvas, BoundsRect);
{Draw buttons}
Font := Self.Font;
if AutoFontSize then
begin
Font.Size := FCalcFontSize;
if Font.Size >= cxCalcMinBoldFontSize then
Font.Style := [fsBold]
else
Font.Style := [];
end;
Brush.Color := Self.Color;
end;
for i := cbBack to cbEqual do
with FButtons[i] do
if RectVisible(Canvas.Handle, BtnRect) then
begin
AState := CalculateButtonState(FButtons[i]);
with GetPainter, Canvas do
begin
DrawButton(Self.Canvas, BtnRect, '', AState);
Font.Color := CalcEditButtonTextColor(Kind);
Brush.Style := bsClear;
if AState = cxbsPressed then
begin
OffsetRect(BtnRect, ButtonTextShift, ButtonTextShift);
DrawText(Text, BtnRect, cxAlignHCenter or cxAlignVCenter or cxSingleLine or
cxShowPrefix, AState <> cxbsDisabled);
OffsetRect(BtnRect, -ButtonTextShift, -ButtonTextShift);
end
else
DrawText(Text, BtnRect, cxAlignHCenter or cxAlignVCenter or cxSingleLine or
cxShowPrefix, AState <> cxbsDisabled);
Brush.Style := bsSolid;
end;
if FFocusRectVisible and IsFocused and (i = cbEqual) then
begin
InflateRect(BtnRect, -3, -3);
Canvas.DrawFocusRect(BtnRect);
InflateRect(BtnRect, 3, 3);
end;
end;
end;
procedure TcxCustomCalculator.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
begin
Style := Style or WS_TABSTOP or WS_CLIPCHILDREN;
WindowClass.Style := WindowClass.Style and not CS_DBLCLKS;
if IsPopupControl then
Style := Style and not WS_BORDER
else
if FBorderStyle = bsSingle then
if NewStyleControls and Ctl3D then
begin
Style := Style and not WS_BORDER;
ExStyle := ExStyle or WS_EX_CLIENTEDGE;
end
else
Style := Style or WS_BORDER;
end;
end;
procedure TcxCustomCalculator.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
var ButtonKind : TcxCalcButtonkind;
begin
if not (csDesigning in ComponentState) and
(CanFocus or (GetParentForm(Self) = nil)) and not IsPopupControl then
SetFocus;
ButtonKind := GetButtonKindAt(X, Y);
if (Button = mbLeft) and (ButtonKind <> cbNone) and not FButtons[ButtonKind].Grayed then
begin
MouseCapture := True;
FTracking := True;
FDownButton := ButtonKind;
TrackButton(X, Y);
end;
inherited MouseDown(Button, Shift, X, Y);
end;
procedure TcxCustomCalculator.MouseMove(Shift: TShiftState; X, Y: Integer);
var OldButton : TcxCalcButtonKind;
begin
if FTracking then
TrackButton(X, Y)
else
if GetPainter.IsButtonHotTrack and Enabled and not Dragging then
begin
OldButton := FActiveButton;
FActiveButton := GetButtonKindAt(X, Y);
if FActiveButton <> OldButton then
begin
if not FButtons[OldButton].Grayed then
InvalidateButton(OldButton);
if not FButtons[FActiveButton].Grayed then
InvalidateButton(FActiveButton);
end;
end;
inherited MouseMove(Shift, X, Y);
end;
procedure TcxCustomCalculator.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
var
WasPressed: Boolean;
begin
WasPressed := (FDownButton <> cbNone) and FButtons[FDownButton].Down;
StopTracking;
if (Button = mbLeft) and WasPressed then
ButtonClick(FDownButton);
FDownButton := cbNone;
inherited MouseUp(Button, Shift, X, Y);
end;
procedure TcxCustomCalculator.KeyDown(var Key: Word; Shift: TShiftState);
var
NewButton, OldButton : TcxCalcButtonKind;
begin
inherited KeyDown(Key, Shift);
OldButton := FPressedButton;
NewButton := GetButtonKindKey(Key, Shift);
if (NewButton <> cbNone) and (OldButton <> NewButton) then
begin
DoButtonUp(OldButton);
FPressedButton := NewButton;
DoButtonDown(FPressedButton);
end;
end;
procedure TcxCustomCalculator.KeyPress(var Key: Char);
var
NewButton, OldButton : TcxCalcButtonKind;
begin
inherited KeyPress(Key);
if (Key = ^V) then
PasteFromClipboard
else
if (Key = ^C) then CopyToClipboard;
OldButton := FPressedButton;
NewButton := GetButtonKindChar(Key);
if (NewButton <> cbNone) and (OldButton <> NewButton) then
begin
DoButtonUp(OldButton);
FPressedButton := NewButton;
DoButtonDown(FPressedButton);
end;
if FPressedButton in RepeatButtons {cbBack} then ButtonClick(FPressedButton);
end;
procedure TcxCustomCalculator.KeyUp(var Key: Word; Shift: TShiftState);
begin
inherited KeyUp(Key, Shift);
DoButtonUp(FPressedButton);
end;
procedure TcxCustomCalculator.Resize;
begin
CalcSize(ClientWidth, ClientHeight);
ClientWidth := FCalcWidth;
ClientHeight := FCalcHeight;
inherited;
end;
procedure TcxCustomCalculator.DoButtonDown(ButtonKind : TcxCalcButtonKind);
begin
if ButtonKind <> cbNone then
begin
FButtons[ButtonKind].Down := True;
InvalidateButton(ButtonKind);
Update;
if not (ButtonKind in RepeatButtons) {cbBack} then ButtonClick(ButtonKind);
end;
end;
procedure TcxCustomCalculator.DoButtonUp(ButtonKind : TcxCalcButtonKind);
begin
if ButtonKind <> cbNone then
begin
FButtons[ButtonKind].Down := False;
InvalidateButton(ButtonKind);
FPressedButton := cbNone;
Update;
end;
end;
function TcxCustomCalculator.GetEditorValue: String;
begin
Result := '';
end;
procedure TcxCustomCalculator.SetEditorValue(const Value: String);
begin
end;
procedure TcxCustomCalculator.CreateLayout;
var
I: TcxCalcButtonKind;
X: Integer;
begin
for I := cbBack to cbEqual do
begin
FButtons[I].Kind := I;
FButtons[I].Text := BtnCaptions[I];
FButtons[I].BtnRect := cxEmptyRect;
FButtons[I].Down := False;
FButtons[I].Grayed := False;
if I = cbDecimal then
FButtons[I].Text := SysUtils.DecimalSeparator
else
FButtons[I].Text := BtnCaptions[i];
end;
{coord buttons}
FButtons[cbMC].BtnRect := Rect(FCalcXOfs,
(FCalcYOfs+FCalcBtnHeight)+FCalcYOfs,
FCalcXOfs+FCalcBtnWidth,
(FCalcYOfs+FCalcBtnHeight)*2);
FButtons[cbMR].BtnRect := Rect(FCalcXOfs,
(FCalcYOfs+FCalcBtnHeight)*2+FCalcYOfs,
FCalcXOfs+FCalcBtnWidth,
(FCalcYOfs+FCalcBtnHeight)*3);
FButtons[cbMS].BtnRect := Rect(FCalcXOfs,
(FCalcYOfs+FCalcBtnHeight)*3+FCalcYOfs,
FCalcXOfs+FCalcBtnWidth,
(FCalcYOfs+FCalcBtnHeight)*4);
FButtons[cbMP].BtnRect := Rect(FCalcXOfs,
(FCalcYOfs+FCalcBtnHeight)*4+FCalcYOfs,
FCalcXOfs+FCalcBtnWidth,
(FCalcYOfs+FCalcBtnHeight)*5);
X := FCalcXOfs+FCalcBtnWidth + FCalcXOfs + 4;
{7, 8, 9, /, sqrt}
FButtons[cbNum7].BtnRect := Rect(X+FCalcXOfs,
(FCalcYOfs+FCalcBtnHeight)+FCalcYOfs,
X+FCalcXOfs+FCalcBtnWidth,
(FCalcYOfs+FCalcBtnHeight)*2);
FButtons[cbNum8].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth),
(FCalcYOfs+FCalcBtnHeight)+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*2,
(FCalcYOfs+FCalcBtnHeight)*2);
FButtons[cbNum9].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth)*2,
(FCalcYOfs+FCalcBtnHeight)+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*3,
(FCalcYOfs+FCalcBtnHeight)*2);
FButtons[cbDiv].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth)*3,
(FCalcYOfs+FCalcBtnHeight)+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*4,
(FCalcYOfs+FCalcBtnHeight)*2);
FButtons[cbSqrt].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth)*4,
(FCalcYOfs+FCalcBtnHeight)+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*5,
(FCalcYOfs+FCalcBtnHeight)*2);
{4, 5, 6, *, %}
FButtons[cbNum4].BtnRect := Rect(X+FCalcXOfs,
(FCalcYOfs+FCalcBtnHeight)*2+FCalcYOfs,
X+FCalcXOfs+FCalcBtnWidth,
(FCalcYOfs+FCalcBtnHeight)*3);
FButtons[cbNum5].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth),
(FCalcYOfs+FCalcBtnHeight)*2+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*2,
(FCalcYOfs+FCalcBtnHeight)*3);
FButtons[cbNum6].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth)*2,
(FCalcYOfs+FCalcBtnHeight)*2+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*3,
(FCalcYOfs+FCalcBtnHeight)*3);
FButtons[cbMul].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth)*3,
(FCalcYOfs+FCalcBtnHeight)*2+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*4,
(FCalcYOfs+FCalcBtnHeight)*3);
FButtons[cbPercent].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth)*4,
(FCalcYOfs+FCalcBtnHeight)*2+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*5,
(FCalcYOfs+FCalcBtnHeight)*3);
{1, 2, 3, -, 1/x}
FButtons[cbNum1].BtnRect := Rect(X+FCalcXOfs,
(FCalcYOfs+FCalcBtnHeight)*3+FCalcYOfs,
X+FCalcXOfs+FCalcBtnWidth,
(FCalcYOfs+FCalcBtnHeight)*4);
FButtons[cbNum2].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth),
(FCalcYOfs+FCalcBtnHeight)*3+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*2,
(FCalcYOfs+FCalcBtnHeight)*4);
FButtons[cbNum3].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth)*2,
(FCalcYOfs+FCalcBtnHeight)*3+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*3,
(FCalcYOfs+FCalcBtnHeight)*4);
FButtons[cbSub].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth)*3,
(FCalcYOfs+FCalcBtnHeight)*3+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*4,
(FCalcYOfs+FCalcBtnHeight)*4);
FButtons[cbRev].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth)*4,
(FCalcYOfs+FCalcBtnHeight)*3+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*5,
(FCalcYOfs+FCalcBtnHeight)*4);
{0, +/-, ., +, =}
FButtons[cbNum0].BtnRect := Rect(X+FCalcXOfs,
(FCalcYOfs+FCalcBtnHeight)*4+FCalcYOfs,
X+FCalcXOfs+FCalcBtnWidth,
(FCalcYOfs+FCalcBtnHeight)*5);
FButtons[cbSign].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth),
(FCalcYOfs+FCalcBtnHeight)*4+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*2,
(FCalcYOfs+FCalcBtnHeight)*5);
FButtons[cbDecimal].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth)*2,
(FCalcYOfs+FCalcBtnHeight)*4+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*3,
(FCalcYOfs+FCalcBtnHeight)*5);
FButtons[cbAdd].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth)*3,
(FCalcYOfs+FCalcBtnHeight)*4+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*4,
(FCalcYOfs+FCalcBtnHeight)*5);
FButtons[cbEqual].BtnRect := Rect(X+FCalcXOfs+(FCalcXOfs+FCalcBtnWidth)*4,
(FCalcYOfs+FCalcBtnHeight)*4+FCalcYOfs,
X+(FCalcXOfs+FCalcBtnWidth)*5,
(FCalcYOfs+FCalcBtnHeight)*5);
{C}
FButtons[cbClear].BtnRect := FButtons[cbEqual].BtnRect;
FButtons[cbClear].BtnRect.Left := FButtons[cbClear].BtnRect.Right - FCalcLargeBtnWidth;
FButtons[cbClear].BtnRect.Top := FCalcYOfs;
FButtons[cbClear].BtnRect.Bottom := FCalcYOfs + FCalcBtnHeight;
{CE}
FButtons[cbCancel].BtnRect := FButtons[cbClear].BtnRect;
FButtons[cbCancel].BtnRect.Right := FButtons[cbClear].BtnRect.Left - FCalcYOfs;
FButtons[cbCancel].BtnRect.Left := FButtons[cbCancel].BtnRect.Right - FCalcLargeBtnWidth;
{Back}
FButtons[cbBack].BtnRect := FButtons[cbCancel].BtnRect;
FButtons[cbBack].BtnRect.Right := FButtons[cbBack].BtnRect.Left - FCalcYOfs;
FButtons[cbBack].BtnRect.Left := FButtons[cbBack].BtnRect.Right - FCalcLargeBtnWidth;
// ResetOperands;
ResetOperands;
// Update Memory display
UpdateMemoryButtons;
end;
procedure TcxCustomCalculator.ResetOperands;
begin
FOperator := cbEqual;
FStatus := csFirst;
FMemory := 0.0;
end;
procedure TcxCustomCalculator.SetBorderStyle(Value: TBorderStyle);
begin
if FBorderStyle <> Value then
begin
FBorderStyle := Value;
RecreateWnd;
end;
end;
procedure TcxCustomCalculator.SetFocusRectVisible(Value : Boolean);
begin
if FFocusRectVisible <> Value then
begin
FFocusRectVisible := Value;
Invalidate;
end;
end;
procedure TcxCustomCalculator.CalcSize(AWidth, AHeight : Integer);
var
h, NearHeight, d, dMin : Integer;
function CalcHeight(ABtnHeight:Integer):Integer;
var FYOfs : Integer;
begin
FYOfs := MulDiv(ABtnHeight, cxMinCalcYOfs, cxMinCalcBtnHeight);
Result := (FYOfs + ABtnHeight) * 5 + FYOfs;
end;
begin
if AutoFontSize then
begin
h := MulDiv(AWidth, cxMinCalcHeight, cxMinCalcWidth);
if AHeight > h then AHeight := h;
{Calculate nearest FCalcHeight }
h := cxMinCalcBtnHeight;
NearHeight := h;
dMin := AHeight;
while True do
begin
d := abs(CalcHeight(h) - AHeight);
if d < dMin then
begin
dMin := d;
NearHeight := h;
end
else
Break;
inc(h);
end;
end
else
NearHeight := Canvas.FontHeight(Font) * 2;
FCalcBtnHeight := NearHeight;
FCalcBtnWidth := MulDiv(FCalcBtnHeight, cxMinCalcBtnWidth, cxMinCalcBtnHeight);
FCalcYOfs := MulDiv(FCalcBtnHeight, cxMinCalcYOfs, cxMinCalcBtnHeight);
FCalcXOfs := FCalcYOfs;
FCalcLargeBtnWidth := MulDiv(FCalcBtnWidth, 17, 10);
FCalcFontSize := MulDiv(FCalcBtnHeight, cxMinCalcFontSize, cxMinCalcBtnHeight);
FCalcHeight := (FCalcYOfs+FCalcBtnHeight)*5+FCalcYOfs;
FCalcWidth := (FCalcXOfs+FCalcBtnWidth)*6+FCalcXOfs*2+4;
// reCalc rect buttons
CreateLayout;
end;
procedure TcxCustomCalculator.FontChanged;
begin
if not (csLoading in ComponentState) then ParentFont := False;
inherited FontChanged;
end;
procedure TcxCustomCalculator.FocusChanged;
begin
inherited FocusChanged;
InvalidateButton(cbEqual);
end;
procedure TcxCustomCalculator.SetEnabled( Value: Boolean);
begin
inherited;
Invalidate;
end;
procedure TcxCustomCalculator.StopTracking;
begin
if FTracking then
begin
TrackButton(-1, -1);
FTracking := False;
MouseCapture := False;
if FDownButton <> cbNone then
FButtons[FDownButton].Down := False;
end;
end;
procedure TcxCustomCalculator.TrackButton(X,Y: Integer);
var
FlagRepaint : Boolean;
begin
if FDownButton <> cbNone then
begin
FlagRepaint := (GetButtonKindAt(X, Y) = FDownButton) <> FButtons[FDownButton].Down;
FButtons[FDownButton].Down := (GetButtonKindAt(X, Y) = FDownButton);
if FlagRepaint then
InvalidateButton(FDownButton);
end;
end;
procedure TcxCustomCalculator.InvalidateButton(ButtonKind : TcxCalcButtonKind);
var
R: TRect;
begin
if ButtonKind <> cbNone then
begin
R := FButtons[ButtonKind].BtnRect;
InvalidateRect(R, False);
end;
end;
procedure TcxCustomCalculator.MouseLeave(AControl: TControl);
begin
inherited;
if GetPainter.IsButtonHotTrack and Enabled and
not Dragging and (FActiveButton <> cbNone) then
begin
InvalidateButton(FActiveButton);
FActiveButton := cbNone;
end;
end;
function TcxCustomCalculator.GetButtonKindChar(Ch : Char) : TcxCalcbuttonKind;
begin
case Ch of
'0'..'9' : Result := TcxCalcbuttonKind(Ord(cbNum0)+Ord(Ch)-Ord('0'));
'+' : Result := cbAdd;
'-' : Result := cbSub;
'*' : Result := cbMul;
'/' : Result := cbDiv;
'%' : Result := cbPercent;
'=' : Result := cbEqual;
#8 : Result := cbBack;
'@' : Result := cbSqrt;
'.', ',': Result := cbDecimal;
else
Result := cbNone;
end;
end;
function TcxCustomCalculator.GetButtonKindKey(Key: Word; Shift: TShiftState) : TcxCalcbuttonKind;
begin
Result := cbNone;
case Key of
VK_RETURN : Result := cbEqual;
VK_ESCAPE : Result := cbClear;
VK_F9 : Result := cbSign;
VK_DELETE : Result := cbCancel;
Ord('C'){VK_C} : if not (ssCtrl in Shift) then Result := cbClear;
Ord('P'){VK_P} : if ssCtrl in Shift then Result := cbMP;
Ord('L'){VK_L} : if ssCtrl in Shift then Result := cbMC;
Ord('R'){VK_R} : if ssCtrl in Shift then Result := cbMR
else Result := cbRev;
Ord('M'){VK_M} : if ssCtrl in Shift then Result := cbMS;
end;
end;
procedure TcxCustomCalculator.CopyToClipboard;
begin
Clipboard.AsText := GetEditorValue;
end;
procedure TcxCustomCalculator.PasteFromClipboard;
var
S, S1 : String;
i : Integer;
begin
if Clipboard.HasFormat(CF_TEXT) then
try
S := Clipboard.AsText;
S1 := '';
repeat
i := Pos(CurrencyString, S);
if i > 0 then
begin
S1 := S1 + Copy(S, 1, i - 1);
S := Copy(S, i + Length(CurrencyString), MaxInt);
end
else
S1 := S1 + S;
until i <= 0;
SetDisplay(StrToFloat(Trim(S1)));
FStatus := csValid;
except
SetDisplay(0.0);
end;
end;
procedure TcxCustomCalculator.SetAutoFontSize(Value : Boolean);
begin
if AutoFontSize <> Value then
begin
FAutoFontSize := Value;
Font.OnChange(nil);
end;
end;
// math routines
procedure TcxCustomCalculator.Error;
begin
FStatus := csError;
SetEditorValue(cxGetResourceString(@scxSCalcError));
if FBeepOnError then MessageBeep(0);
// if Assigned(FOnError) then FOnError(Self);
end;
procedure TcxCustomCalculator.CheckFirst;
begin
if FStatus = csFirst then
begin
FStatus := csValid;
SetEditorValue('0');
end;
end;
procedure TcxCustomCalculator.Clear;
begin
FStatus := csFirst;
SetDisplay(0.0);
FOperator := cbEqual;
end;
procedure TcxCustomCalculator.ButtonClick(ButtonKind : TcxCalcButtonKind);
var
AValue : Extended;
AOldEditorValue: string;
begin
if Assigned(FOnButtonClick) then FOnButtonClick(Self, ButtonKind);
if (FStatus = csError) and not (ButtonKind in [cbClear, cbCancel]) then
begin
Error;
Exit;
end;
AOldEditorValue := EditorValue;
LockChanges(True);
try
case ButtonKind of
cbDecimal:
begin
CheckFirst;
if Pos(DecimalSeparator, EditorValue) = 0 then
SetEditorValue(EditorValue + DecimalSeparator);
end;
cbRev:
if FStatus in [csValid, csFirst] then
begin
FStatus := csFirst;
if FOperator in OperationButtons then
FStatus := csValid;
if GetDisplay = 0 then Error else SetDisplay(1.0 / GetDisplay);
end;
cbSqrt:
if FStatus in [csValid, csFirst] then
begin
FStatus := csFirst;
if FOperator in OperationButtons then
FStatus := csValid;
if GetDisplay < 0 then Error else SetDisplay(Sqrt(GetDisplay));
end;
cbNum0..cbNum9:
begin
CheckFirst;
if EditorValue = '0' then SetEditorValue('');
if Length(EditorValue) < Max(2, FPrecision) + Ord(Boolean(Pos('-', EditorValue))) then
SetEditorValue(EditorValue + Char(Ord('0')+Byte(ButtonKind)-Byte(cbNum0)))
else
if FBeepOnError then MessageBeep(0);
end;
cbBack:
begin
CheckFirst;
if (Length(EditorValue) = 1) or ((Length(EditorValue) = 2) and (EditorValue[1] = '-')) then
SetEditorValue('0')
else
SetEditorValue(Copy(EditorValue, 1, Length(EditorValue) - 1));
end;
cbSign: SetDisplay(-GetDisplay);
cbAdd, cbSub, cbMul, cbDiv, cbEqual, cbPercent :
begin
if FStatus = csValid then
begin
FStatus := csFirst;
AValue := GetDisplay;
if ButtonKind = cbPercent then
case FOperator of
cbAdd, cbSub : AValue := FOperand * AValue / 100.0;
cbMul, cbDiv : AValue := AValue / 100.0;
end;
case FOperator of
cbAdd : SetDisplay(FOperand + AValue);
cbSub : SetDisplay(FOperand - AValue);
cbMul : SetDisplay(FOperand * AValue);
cbDiv : if AValue = 0 then Error else SetDisplay(FOperand / AValue);
end;
end;
FOperator := ButtonKind;
FOperand := GetDisplay;
if (ButtonKind in ResultButtons) and Assigned(FOnResult) then FOnResult(Self);
end;
cbClear, cbCancel: Clear;
cbMP:
if FStatus in [csValid, csFirst] then
begin
FStatus := csFirst;
FMemory := FMemory + GetDisplay;
UpdateMemoryButtons;
InvalidateMemoryButtons;
end;
cbMS:
if FStatus in [csValid, csFirst] then
begin
FStatus := csFirst;
FMemory := GetDisplay;
UpdateMemoryButtons;
InvalidateMemoryButtons;
end;
cbMR:
if FStatus in [csValid, csFirst] then
begin
FStatus := csFirst;
CheckFirst;
SetDisplay(FMemory);
end;
cbMC:
begin
FMemory := 0.0;
UpdateMemoryButtons;
InvalidateMemoryButtons;
end;
end;
finally
LockChanges(False, AOldEditorValue <> EditorValue);
end;
end;
procedure TcxCustomCalculator.UpdateMemoryButtons;
begin
// Disable buttons
if FMemory <> 0.0 then
begin
FButtons[cbMC].Grayed := False;
FButtons[cbMR].Grayed := False;
end
else
begin
FButtons[cbMC].Grayed := True;
FButtons[cbMR].Grayed := True;
end;
end;
procedure TcxCustomCalculator.InvalidateMemoryButtons;
begin
InvalidateButton(cbMC);
InvalidateButton(cbMR);
end;
function TcxCustomCalculator.GetDisplay: Extended;
var
S: string;
begin
if FStatus = csError then
Result := 0.0
else
begin
S := Trim(GetEditorValue);
if S = '' then S := '0';
RemoveThousandSeparator(S);
Result := StrToFloat(S);
end;
end;
procedure TcxCustomCalculator.SetDisplay(Value: Extended);
var
S: string;
begin
S := FloatToStrF(Value, ffGeneral, Max(2, FPrecision), 0);
if GetEditorValue <> S then
begin
SetEditorValue(S);
if Assigned(FOnDisplayChange) then FOnDisplayChange(Self);
end;
end;
function TcxCustomCalculator.GetMemory: Extended;
begin
Result := FMemory;
end;
procedure TcxCustomCalculator.HidePopup(Sender: TcxControl; AReason: TcxEditCloseUpReason);
begin
if Assigned(FOnHidePopup) then FOnHidePopup(Self, AReason);
end;
procedure TcxCustomCalculator.LockChanges(ALock: Boolean; AInvokeChangedOnUnlock: Boolean = True);
begin
end;
{ TcxPopupCalculator }
constructor TcxPopupCalculator.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
IsPopupControl := True;
end;
procedure TcxPopupCalculator.Init;
begin
FPressedButton := cbNone;
end;
function TcxPopupCalculator.GetEditorValue: string;
begin
Result := Edit.Text;
end;
function TcxPopupCalculator.GetPainter: TcxCustomLookAndFeelPainterClass;
begin
if Edit.ViewInfo.Painter <> nil then
Result := Edit.ViewInfo.Painter
else
Result := GetButtonPainterClass(Edit.PopupControlsLookAndFeel);
end;
procedure TcxPopupCalculator.KeyDown(var Key: Word; Shift: TShiftState);
begin
inherited KeyDown(Key, Shift);
case Key of
VK_ESCAPE: HidePopup(Self, crCancel);
VK_INSERT:
if (Shift = [ssShift]) then
PasteFromClipboard
else
if (Shift = [ssCtrl]) then
CopyToClipboard;
VK_F4:
if not (ssAlt in Shift) then
HidePopup(Self, crClose);
VK_UP, VK_DOWN:
if Shift = [ssAlt] then
HidePopup(Self, crClose);
VK_TAB:
Edit.DoEditKeyDown(Key, Shift);
VK_RETURN:
HidePopup(Self, crEnter);
end;
end;
procedure TcxPopupCalculator.KeyPress(var Key: Char);
begin
inherited KeyPress(Key);
if (Key = '=') and FEdit.ActiveProperties.QuickClose then
HidePopup(Self, crEnter);
end;
procedure TcxPopupCalculator.SetEditorValue(const Value: string);
begin
if Edit.DoEditing then
begin
Edit.InnerEdit.EditValue := Value;
Edit.ModifiedAfterEnter := True;
end;
end;
procedure TcxPopupCalculator.LockChanges(ALock: Boolean; AInvokeChangedOnUnlock: Boolean = True);
begin
inherited;
Edit.LockChangeEvents(ALock, AInvokeChangedOnUnlock);
end;
{ TcxCalcEditPropertiesValues }
procedure TcxCalcEditPropertiesValues.Assign(Source: TPersistent);
begin
if Source is TcxCalcEditPropertiesValues then
begin
BeginUpdate;
try
inherited Assign(Source);
Precision := TcxCalcEditPropertiesValues(Source).Precision;
finally
EndUpdate;
end;
end
else
inherited Assign(Source);
end;
procedure TcxCalcEditPropertiesValues.RestoreDefaults;
begin
BeginUpdate;
try
inherited RestoreDefaults;
Precision := False;
finally
EndUpdate;
end;
end;
procedure TcxCalcEditPropertiesValues.SetPrecision(Value: Boolean);
begin
if Value <> FPrecision then
begin
FPrecision := Value;
Changed;
end;
end;
{ TcxCustomCalcEditProperties }
constructor TcxCustomCalcEditProperties.Create(AOwner: TPersistent);
begin
inherited Create(AOwner);
FBeepOnError := True;
FPrecision := cxDefCalcPrecision;
// MaxLength := cxDefCalcPrecision + 2;
FQuickClose := False;
PopupSizeable := False;
ImmediateDropDown := False;
end;
function TcxCustomCalcEditProperties.GetAssignedValues: TcxCalcEditPropertiesValues;
begin
Result := TcxCalcEditPropertiesValues(FAssignedValues);
end;
function TcxCustomCalcEditProperties.GetPrecision: Byte;
begin
if AssignedValues.Precision then
Result := FPrecision
else
if IDefaultValuesProvider <> nil then
Result := IDefaultValuesProvider.DefaultPrecision
else
Result := cxDefCalcPrecision;
if Result > cxMaxCalcPrecision then
Result := cxMaxCalcPrecision;
end;
function TcxCustomCalcEditProperties.IsPrecisionStored: Boolean;
begin
Result := AssignedValues.Precision;
end;
procedure TcxCustomCalcEditProperties.SetAssignedValues(
Value: TcxCalcEditPropertiesValues);
begin
FAssignedValues.Assign(Value);
end;
procedure TcxCustomCalcEditProperties.SetBeepOnError(Value: Boolean);
begin
if Value <> FBeepOnError then
begin
FBeepOnError := Value;
Changed;
end;
end;
procedure TcxCustomCalcEditProperties.SetPrecision(Value: Byte);
begin
if AssignedValues.Precision and (Value = FPrecision) then
Exit;
AssignedValues.FPrecision := True;
FPrecision := Value;
Changed;
end;
procedure TcxCustomCalcEditProperties.SetQuickClose(Value: Boolean);
begin
if Value <> FQuickClose then
begin
FQuickClose := Value;
Changed;
end;
end;
procedure TcxCustomCalcEditProperties.SetScientificFormat(Value: Boolean);
begin
if Value <> FScientificFormat then
begin
FScientificFormat := Value;
Changed;
end;
end;
procedure TcxCustomCalcEditProperties.SetUseThousandSeparator(Value: Boolean);
begin
if Value <> FUseThousandSeparator then
begin
FUseThousandSeparator := Value;
Changed;
end;
end;
function TcxCustomCalcEditProperties.GetAlwaysPostEditValue: Boolean;
begin
Result := True;
end;
class function TcxCustomCalcEditProperties.GetAssignedValuesClass: TcxCustomEditPropertiesValuesClass;
begin
Result := TcxCalcEditPropertiesValues;
end;
function TcxCustomCalcEditProperties.HasDigitGrouping(
AIsDisplayValueSynchronizing: Boolean): Boolean;
begin
Result := not ScientificFormat and UseThousandSeparator;
end;
function TcxCustomCalcEditProperties.PopupWindowAcceptsAnySize: Boolean;
begin
Result := False;
end;
function TcxCustomCalcEditProperties.StrToFloatEx(S: string;
var Value: Extended): Boolean;
var
E: Extended;
I: Integer;
begin
// Ignore Thousand Separators
for I := Length(S) downto 1 do
if S[I] = ThousandSeparator then
Delete(S, I, 1);
if not TextToFloat(PChar(S), E, fvExtended) or
((E <> 0) and ((Abs(E) < MinDouble) or (Abs(E) > MaxDouble))) then
begin
Value := 0;
Result := False;
end
else
begin
Value := E;
Result := True;
end;
end;
procedure TcxCustomCalcEditProperties.Assign(Source: TPersistent);
begin
if Source is TcxCustomCalcEditProperties then
begin
BeginUpdate;
try
inherited Assign(Source);
with TcxCustomCalcEditProperties(Source) do
begin
Self.BeepOnError := BeepOnError;
Self.AssignedValues.Precision := False;
if AssignedValues.Precision then
Self.Precision := Precision;
Self.QuickClose := QuickClose;
Self.ScientificFormat := ScientificFormat;
Self.UseThousandSeparator := UseThousandSeparator;
end;
finally
EndUpdate
end
end
else
inherited Assign(Source);
end;
class function TcxCustomCalcEditProperties.GetContainerClass: TcxContainerClass;
begin
Result := TcxCalcEdit;
end;
function TcxCustomCalcEditProperties.GetEditValueSource(AEditFocused: Boolean): TcxDataEditValueSource;
begin
if not AEditFocused and not AssignedValues.DisplayFormat and
(IDefaultValuesProvider <> nil) and
IDefaultValuesProvider.IsDisplayFormatDefined(True) then
Result := evsText
else
Result := evsValue;
end;
function TcxCustomCalcEditProperties.IsDisplayValueValid(
var DisplayValue: TcxEditValue; AEditFocused: Boolean): Boolean;
begin
// if AEditFocused
Result := True;
end;
function TcxCustomCalcEditProperties.IsEditValueValid(var EditValue: TcxEditValue;
AEditFocused: Boolean): Boolean;
var
AValue: Extended;
begin
Result := VarIsNumericEx(EditValue) or VarIsSoftNull(EditValue);
if not Result then
Result := VarIsStr(EditValue) and
TextToFloat(PChar(VarToStr(EditValue)), AValue, fvExtended);
end;
procedure TcxCustomCalcEditProperties.PrepareDisplayValue(
const AEditValue: TcxEditValue; var DisplayValue: TcxEditValue;
AEditFocused: Boolean);
procedure RemoveInsignificantZeros(var S: string);
var
AExponentialPart: string;
I: Integer;
begin
if Pos(DecimalSeparator, S) = 0 then
Exit;
AExponentialPart := RemoveExponentialPart(S);
I := Length(S);
while S[I] = '0' do
Dec(I);
Delete(S, I + 1, Length(S) - I);
if S[Length(S)] = DecimalSeparator then
Delete(S, Length(S), 1);
S := S + AExponentialPart;
end;
var
AFormat: TFloatFormat;
APrecision: Byte;
S: string;
begin
if VarIsSoftNull(AEditValue) then
S := ''
else
if not AEditFocused and AssignedValues.DisplayFormat then
S := FormatFloat(DisplayFormat, AEditValue)
else
begin
if ScientificFormat then
AFormat := ffExponent
else
AFormat := ffGeneral;
APrecision := Precision;
if APrecision = 0 then
APrecision := cxDefCalcPrecision;
S := FloatToStrF(AEditValue, AFormat, APrecision, 0);
if UseThousandSeparator and not ScientificFormat then
InsertThousandSeparator(S);
if ScientificFormat then
RemoveInsignificantZeros(S);
end;
DisplayValue := S;
end;
procedure TcxCustomCalcEditProperties.ValidateDisplayValue(
var ADisplayValue: TcxEditValue; var AErrorText: TCaption;
var Error: Boolean; AEdit: TcxCustomEdit);
begin
Error := False;
inherited ValidateDisplayValue(ADisplayValue, AErrorText, Error, AEdit);
end;
{ TcxCustomCalcEdit }
destructor TcxCustomCalcEdit.Destroy;
begin
FreeAndNil(FCalculator);
inherited Destroy;
end;
function TcxCustomCalcEdit.GetProperties: TcxCustomCalcEditProperties;
begin
Result := TcxCustomCalcEditProperties(FProperties);
end;
function TcxCustomCalcEdit.GetActiveProperties: TcxCustomCalcEditProperties;
begin
Result := TcxCustomCalcEditProperties(InternalGetActiveProperties);
end;
function TcxCustomCalcEdit.GetValue: Extended;
begin
if VarIsNull(EditValue) or (VarIsStr(EditValue) and (EditValue = '')) then
Result := 0
else
Result := EditValue;
end;
procedure TcxCustomCalcEdit.SetProperties(Value: TcxCustomCalcEditProperties);
begin
FProperties.Assign(Value);
end;
procedure TcxCustomCalcEdit.SetValue(const Value: Extended);
begin
InternalEditValue := Value;
end;
procedure TcxCustomCalcEdit.FormatChanged;
begin
DataBinding.UpdateDisplayValue;
end;
function TcxCustomCalcEdit.CanDropDown: Boolean;
begin
Result := (not ActiveProperties.ReadOnly) and DataBinding.IsDataAvailable;
end;
procedure TcxCustomCalcEdit.CreatePopupWindow;
begin
inherited CreatePopupWindow;
PopupWindow.ModalMode := False;
end;
procedure TcxCustomCalcEdit.DoEnter;
begin
SynchronizeDisplayValue;
inherited;
end;
procedure TcxCustomCalcEdit.DoExit;
begin
inherited;
DataBinding.UpdateDisplayValue;
end;
procedure TcxCustomCalcEdit.DoInitPopup;
begin
inherited DoInitPopup;
ActiveProperties.PopupControl := FCalculator;
end;
procedure TcxCustomCalcEdit.Initialize;
begin
inherited Initialize;
Value := 0;
FCalculator := TcxPopupCalculator.Create(Self);
FCalculator.Parent := PopupWindow;
FCalculator.Edit := Self;
FCalculator.AutoFontSize := False;
FCalculator.OnHidePopup := HidePopup;
ActiveProperties.PopupControl := FCalculator;
end;
procedure TcxCustomCalcEdit.InitializePopupWindow;
begin
inherited InitializePopupWindow;
with Calculator do
begin
HandleNeeded;
Font.Assign(Self.VisibleFont);
FontChanged;
Resize;
end;
end;
function TcxCustomCalcEdit.InternalGetEditingValue: TcxEditValue;
begin
PrepareEditValue(Text, Result, True);
end;
function TcxCustomCalcEdit.IsValidChar(Key: Char): Boolean;
function NumDigits(const S: string): Byte;
var
I: Integer;
begin
Result := 0;
for I := 1 to Length(S) do
if (S[I] = 'e') or (S[I] = 'E') then
Break
else
if dxCharInSet(S[I], ['0'..'9']) then
Inc(Result);
end;
var
S: string;
V: Extended;
StartPos, StopPos: Integer;
begin
Result := False;
if not IsNumericChar(Key, ntExponent) then
Exit;
S := Text;
StartPos := SelStart;
StopPos := SelStart + SelLength;
Delete(S, SelStart + 1, StopPos - StartPos);
if (Key = '-') and (S = '') then
begin
Result := True;
Exit;
end;
Insert(Key, S, StartPos + 1);
Result := ActiveProperties.StrToFloatEx(S, V);
end;
procedure TcxCustomCalcEdit.KeyPress(var Key: Char);
begin
if (Key = '.') or (Key = ',') then
Key := DecimalSeparator;
if IsTextChar(Key) and not IsValidChar(Key) then
begin
Key := #0;
if ActiveProperties.BeepOnError then Beep;
end;
inherited KeyPress(Key);
end;
procedure TcxCustomCalcEdit.PopupWindowClosed(Sender: TObject);
begin
if Text = cxGetResourceString(@scxSCalcError) then InternalEditValue := 0;
if ActiveProperties.AutoSelect then SelectAll else SelStart := Length(Text);
inherited PopupWindowClosed(Sender);
end;
procedure TcxCustomCalcEdit.PopupWindowShowed(Sender: TObject);
begin
inherited PopupWindowShowed(Sender);
FCalculator.Init;
end;
procedure TcxCustomCalcEdit.PropertiesChanged(Sender: TObject);
begin
if (Sender <> nil) and ActiveProperties.FormatChanging then
Exit;
inherited PropertiesChanged(Sender);
if not PropertiesChangeLocked then
begin
FCalculator.BeepOnError := ActiveProperties.BeepOnError;
FCalculator.Precision := ActiveProperties.Precision;
ActiveProperties.FChangedLocked := True;
ActiveProperties.PopupControl := FCalculator;
ActiveProperties.FChangedLocked := False;
end;
end;
function TcxCustomCalcEdit.InternalPrepareEditValue(const ADisplayValue: string;
out EditValue: TcxEditValue): Boolean;
var
AValue: Extended;
S: string;
begin
Result := True;
S := VarToStr(ADisplayValue);
if not ActiveProperties.ScientificFormat and ActiveProperties.UseThousandSeparator then
RemoveThousandSeparator(S);
if Trim(S) = '' then
EditValue := Null
else
begin
Result := TextToFloat(PChar(S), AValue, fvExtended);
if Result then
EditValue := AValue
else
EditValue := Null;
end;
end;
class function TcxCustomCalcEdit.GetPropertiesClass: TcxCustomEditPropertiesClass;
begin
Result := TcxCustomCalcEditProperties;
end;
procedure TcxCustomCalcEdit.PasteFromClipboard;
begin
if DoEditing then
Calculator.PasteFromClipboard;
end;
procedure TcxCustomCalcEdit.PrepareEditValue(
const ADisplayValue: TcxEditValue; out EditValue: TcxEditValue;
AEditFocused: Boolean);
begin
InternalPrepareEditValue(ADisplayValue, EditValue);
end;
{ TcxCalcEdit }
class function TcxCalcEdit.GetPropertiesClass: TcxCustomEditPropertiesClass;
begin
Result := TcxCalcEditProperties;
end;
function TcxCalcEdit.GetActiveProperties: TcxCalcEditProperties;
begin
Result := TcxCalcEditProperties(InternalGetActiveProperties);
end;
function TcxCalcEdit.GetProperties: TcxCalcEditProperties;
begin
Result := TcxCalcEditProperties(FProperties);
end;
procedure TcxCalcEdit.SetProperties(Value: TcxCalcEditProperties);
begin
FProperties.Assign(Value);
end;
{ TcxFilterCalcEditHelper }
class function TcxFilterCalcEditHelper.GetFilterEditClass: TcxCustomEditClass;
begin
Result := TcxCalcEdit;
end;
class function TcxFilterCalcEditHelper.GetSupportedFilterOperators(
AProperties: TcxCustomEditProperties;
AValueTypeClass: TcxValueTypeClass;
AExtendedSet: Boolean = False): TcxFilterControlOperators;
begin
Result := [fcoEqual, fcoNotEqual, fcoLess, fcoLessEqual, fcoGreater,
fcoGreaterEqual, fcoBlanks, fcoNonBlanks];
if AExtendedSet then
Result := Result + [fcoBetween, fcoNotBetween, fcoInList, fcoNotInList];
end;
class procedure TcxFilterCalcEditHelper.InitializeProperties(AProperties,
AEditProperties: TcxCustomEditProperties; AHasButtons: Boolean);
begin
inherited InitializeProperties(AProperties, AEditProperties, AHasButtons);
with TcxCustomCalcEditProperties(AProperties) do
QuickClose := True;
end;
initialization
GetRegisteredEditProperties.Register(TcxCalcEditProperties, scxSEditRepositoryCalcItem);
FilterEditsController.Register(TcxCalcEditProperties, TcxFilterCalcEditHelper);
finalization
FilterEditsController.Unregister(TcxCalcEditProperties, TcxFilterCalcEditHelper);
end.