977 lines
26 KiB
ObjectPascal
977 lines
26 KiB
ObjectPascal
{ rxspin unit
|
|
|
|
Copyright (C) 2005-2017 Lagunov Aleksey alexs75@yandex.ru and Lazarus team
|
|
original conception from rx library for Delphi (c)
|
|
|
|
This library is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU Library General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or (at your
|
|
option) any later version with the following modification:
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent modules,and
|
|
to copy and distribute the resulting executable under terms of your choice,
|
|
provided that you also meet, for each linked independent module, the terms
|
|
and conditions of the license of that module. An independent module is a
|
|
module which is not derived from or based on this library. If you modify
|
|
this library, you may extend this exception to your version of the library,
|
|
but you are not obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; if not, write to the Free Software Foundation,
|
|
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
}
|
|
|
|
unit rxspin;
|
|
|
|
interface
|
|
|
|
{$I rx.inc}
|
|
|
|
uses ComCtrls, LCLIntf, LCLType, Controls, ExtCtrls, Classes,
|
|
Graphics, LMessages, Forms, StdCtrls, Menus, SysUtils, Messages;
|
|
|
|
type
|
|
|
|
{ TRxSpinButton }
|
|
|
|
TSpinButtonState = (sbNotDown, sbTopDown, sbBottomDown);
|
|
|
|
TRxSpinButton = class(TGraphicControl)
|
|
private
|
|
FDown: TSpinButtonState;
|
|
FUpBitmap: TBitmap;
|
|
FDownBitmap: TBitmap;
|
|
FDragging: Boolean;
|
|
FInvalidate: Boolean;
|
|
FTopDownBtn: TBitmap;
|
|
FBottomDownBtn: TBitmap;
|
|
FRepeatTimer: TTimer;
|
|
FNotDownBtn: TBitmap;
|
|
FLastDown: TSpinButtonState;
|
|
FFocusControl: TWinControl;
|
|
FOnTopClick: TNotifyEvent;
|
|
FOnBottomClick: TNotifyEvent;
|
|
procedure TopClick;
|
|
procedure BottomClick;
|
|
procedure GlyphChanged(Sender: TObject);
|
|
function GetUpGlyph: TBitmap;
|
|
function GetDownGlyph: TBitmap;
|
|
procedure SetUpGlyph(Value: TBitmap);
|
|
procedure SetDownGlyph(Value: TBitmap);
|
|
procedure SetDown(Value: TSpinButtonState);
|
|
procedure SetFocusControl(Value: TWinControl);
|
|
procedure DrawAllBitmap;
|
|
procedure DrawBitmap(ABitmap: TBitmap; ADownState: TSpinButtonState);
|
|
procedure TimerExpired(Sender: TObject);
|
|
procedure CMEnabledChanged(var Message: TLMessage); message CM_ENABLEDCHANGED;
|
|
protected
|
|
procedure Paint; override;
|
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
|
|
X, Y: Integer); override;
|
|
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
|
|
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
|
|
X, Y: Integer); override;
|
|
procedure Notification(AComponent: TComponent;
|
|
Operation: TOperation); override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
property Down: TSpinButtonState read FDown write SetDown default sbNotDown;
|
|
published
|
|
property DragCursor;
|
|
property DragMode;
|
|
property Enabled;
|
|
property Visible;
|
|
property DownGlyph: TBitmap read GetDownGlyph write SetDownGlyph;
|
|
property UpGlyph: TBitmap read GetUpGlyph write SetUpGlyph;
|
|
property FocusControl: TWinControl read FFocusControl write SetFocusControl;
|
|
property ShowHint;
|
|
property ParentShowHint;
|
|
{$IFDEF RX_D4}
|
|
property Anchors;
|
|
property Constraints;
|
|
property DragKind;
|
|
{$ENDIF}
|
|
property OnBottomClick: TNotifyEvent read FOnBottomClick write FOnBottomClick;
|
|
property OnTopClick: TNotifyEvent read FOnTopClick write FOnTopClick;
|
|
property OnDragDrop;
|
|
property OnDragOver;
|
|
property OnEndDrag;
|
|
{$IFDEF RX_D4}
|
|
property OnEndDock;
|
|
property OnStartDock;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ TRxSpinEdit }
|
|
|
|
TValueType = (vtInteger, vtFloat, vtHex);
|
|
|
|
TRxSpinEdit = class(TCustomEdit)
|
|
private
|
|
FAlignment: TAlignment;
|
|
FMinValue: Extended;
|
|
FMaxValue: Extended;
|
|
FIncrement: Extended;
|
|
FDecimal: Byte;
|
|
FChanging: Boolean;
|
|
FEditorEnabled: Boolean;
|
|
FValueType: TValueType;
|
|
FButton: TRxSpinButton;
|
|
FBtnWindow: TWinControl;
|
|
FArrowKeys: Boolean;
|
|
FOnTopClick: TNotifyEvent;
|
|
FOnBottomClick: TNotifyEvent;
|
|
function GetMinHeight: Integer;
|
|
procedure GetTextHeight(var SysHeight, aHeight: Integer);
|
|
function GetValue: Extended;
|
|
function CheckValue(NewValue: Extended): Extended;
|
|
function GetAsInteger: Longint;
|
|
function IsIncrementStored: Boolean;
|
|
function IsMaxStored: Boolean;
|
|
function IsMinStored: Boolean;
|
|
function IsValueStored: Boolean;
|
|
procedure SetArrowKeys(Value: Boolean);
|
|
procedure SetAsInteger(NewValue: Longint);
|
|
procedure SetValue(NewValue: Extended);
|
|
procedure SetValueType(NewType: TValueType);
|
|
procedure SetDecimal(NewValue: Byte);
|
|
function GetButtonWidth: Integer;
|
|
procedure RecreateButton;
|
|
procedure ResizeButton;
|
|
procedure SetAlignment(Value: TAlignment);
|
|
procedure LMSize(var Message: TLMSize); message LM_SIZE;
|
|
procedure CMEnter(var Message: TLMessage); message CM_ENTER;
|
|
procedure CMExit(var Message: TLMExit); message CM_EXIT;
|
|
procedure WMPaste(var Message: TLMessage); message LM_PASTE;
|
|
procedure WMCut(var Message: TLMessage); message LM_CUT;
|
|
// procedure CMCtl3DChanged(var Message: TLMessage); message CM_CTL3DCHANGED;
|
|
procedure CMEnabledChanged(var Message: TLMessage); message CM_ENABLEDCHANGED;
|
|
procedure CMFontChanged(var Message: TLMessage); message CM_FONTCHANGED;
|
|
procedure CheckButtonVisible;
|
|
procedure WMSetFocus(var Message: TLMSetFocus); message LM_SETFOCUS;
|
|
protected
|
|
procedure Change; override;
|
|
function IsValidChar(Key: Char): Boolean; virtual;
|
|
procedure UpClick(Sender: TObject); virtual;
|
|
procedure DownClick(Sender: TObject); virtual;
|
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
|
procedure KeyPress(var Key: Char); override;
|
|
procedure CreateParams(var Params: TCreateParams); override;
|
|
procedure CreateWnd; override;
|
|
// Added from TEditButton
|
|
procedure SetParent(AParent: TWinControl); override;
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
|
procedure Loaded; override;
|
|
procedure CMVisibleChanged(var Msg: TLMessage); message CM_VISIBLECHANGED;
|
|
//
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
property AsInteger: Longint read GetAsInteger write SetAsInteger default 0;
|
|
property Text;
|
|
published
|
|
property Alignment: TAlignment read FAlignment write SetAlignment
|
|
default taLeftJustify;
|
|
property ArrowKeys: Boolean read FArrowKeys write SetArrowKeys default True;
|
|
property Decimal: Byte read FDecimal write SetDecimal default 2;
|
|
property EditorEnabled: Boolean read FEditorEnabled write FEditorEnabled default True;
|
|
property Increment: Extended read FIncrement write FIncrement stored IsIncrementStored;
|
|
property MaxValue: Extended read FMaxValue write FMaxValue stored IsMaxStored;
|
|
property MinValue: Extended read FMinValue write FMinValue stored IsMinStored;
|
|
property ValueType: TValueType read FValueType write SetValueType default vtInteger;
|
|
property Value: Extended read GetValue write SetValue stored IsValueStored;
|
|
property AutoSelect;
|
|
property AutoSize;
|
|
property BorderStyle;
|
|
property Color;
|
|
// property Ctl3D;
|
|
property DragCursor;
|
|
property DragMode;
|
|
property Enabled;
|
|
property Font;
|
|
property Anchors;
|
|
property BiDiMode;
|
|
property Constraints;
|
|
property DragKind;
|
|
property ParentBiDiMode;
|
|
property MaxLength;
|
|
property ParentColor;
|
|
// property ParentCtl3D;
|
|
property ParentFont;
|
|
property ParentShowHint;
|
|
property PopupMenu;
|
|
property ReadOnly;
|
|
property ShowHint;
|
|
property TabOrder;
|
|
property TabStop;
|
|
property Visible;
|
|
property OnBottomClick: TNotifyEvent read FOnBottomClick write FOnBottomClick;
|
|
property OnTopClick: TNotifyEvent read FOnTopClick write FOnTopClick;
|
|
property OnChange;
|
|
property OnClick;
|
|
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 OnMouseWheel;
|
|
property OnMouseWheelDown;
|
|
property OnMouseWheelUp;
|
|
property OnContextPopup;
|
|
property OnEndDock;
|
|
property OnStartDock;
|
|
//{$ENDIF}
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
rxlclutils, LResources;
|
|
|
|
const
|
|
sSpinUpBtn = 'RXSPINUP';
|
|
sSpinDownBtn = 'RXSPINDOWN';
|
|
|
|
const
|
|
InitRepeatPause = 400; { pause before repeat timer (ms) }
|
|
RepeatPause = 100;
|
|
|
|
{ TRxSpinButton }
|
|
|
|
constructor TRxSpinButton.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
{ FUpBitmap := TBitmap.Create;
|
|
FDownBitmap := TBitmap.Create;}
|
|
FUpBitmap := LoadBitmapFromLazarusResource(sSpinUpBtn);
|
|
FDownBitmap := LoadBitmapFromLazarusResource(sSpinDownBtn);
|
|
FUpBitmap.OnChange := @GlyphChanged;
|
|
FDownBitmap.OnChange := @GlyphChanged;
|
|
Height := 20;
|
|
Width := 20;
|
|
FTopDownBtn := TBitmap.Create;
|
|
FBottomDownBtn := TBitmap.Create;
|
|
FNotDownBtn := TBitmap.Create;
|
|
DrawAllBitmap;
|
|
FLastDown := sbNotDown;
|
|
end;
|
|
|
|
destructor TRxSpinButton.Destroy;
|
|
begin
|
|
FTopDownBtn.Free;
|
|
FBottomDownBtn.Free;
|
|
FNotDownBtn.Free;
|
|
FUpBitmap.Free;
|
|
FDownBitmap.Free;
|
|
FRepeatTimer.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TRxSpinButton.GlyphChanged(Sender: TObject);
|
|
begin
|
|
FInvalidate := True;
|
|
Invalidate;
|
|
end;
|
|
|
|
function TRxSpinButton.GetUpGlyph: TBitmap;
|
|
begin
|
|
Result := FUpBitmap;
|
|
end;
|
|
|
|
procedure TRxSpinButton.SetUpGlyph(Value: TBitmap);
|
|
begin
|
|
if Value <> nil then FUpBitmap.Assign(Value)
|
|
else
|
|
FUpBitmap := LoadBitmapFromLazarusResource(sSpinUpBtn);
|
|
end;
|
|
|
|
function TRxSpinButton.GetDownGlyph: TBitmap;
|
|
begin
|
|
Result := FDownBitmap;
|
|
end;
|
|
|
|
procedure TRxSpinButton.SetDownGlyph(Value: TBitmap);
|
|
begin
|
|
if Value <> nil then FDownBitmap.Assign(Value)
|
|
else
|
|
FDownBitmap := LoadBitmapFromLazarusResource(sSpinDownBtn);
|
|
end;
|
|
|
|
procedure TRxSpinButton.SetDown(Value: TSpinButtonState);
|
|
var
|
|
OldState: TSpinButtonState;
|
|
begin
|
|
OldState := FDown;
|
|
FDown := Value;
|
|
if OldState <> FDown then Repaint;
|
|
end;
|
|
|
|
procedure TRxSpinButton.SetFocusControl(Value: TWinControl);
|
|
begin
|
|
FFocusControl := Value;
|
|
if Value <> nil then
|
|
Value.FreeNotification(Self);
|
|
end;
|
|
|
|
procedure TRxSpinButton.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if (Operation = opRemove) and (AComponent = FFocusControl) then
|
|
FFocusControl := nil;
|
|
end;
|
|
|
|
procedure TRxSpinButton.Paint;
|
|
begin
|
|
if not Enabled and not (csDesigning in ComponentState) then
|
|
FDragging := False;
|
|
if (FNotDownBtn.Height <> Height) or (FNotDownBtn.Width <> Width) or
|
|
FInvalidate then DrawAllBitmap;
|
|
FInvalidate := False;
|
|
with Canvas do
|
|
case FDown of
|
|
sbNotDown: Draw(0, 0, FNotDownBtn);
|
|
sbTopDown: Draw(0, 0, FTopDownBtn);
|
|
sbBottomDown: Draw(0, 0, FBottomDownBtn);
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinButton.DrawAllBitmap;
|
|
begin
|
|
DrawBitmap(FTopDownBtn, sbTopDown);
|
|
DrawBitmap(FBottomDownBtn, sbBottomDown);
|
|
DrawBitmap(FNotDownBtn, sbNotDown);
|
|
end;
|
|
|
|
procedure TRxSpinButton.DrawBitmap(ABitmap: TBitmap; ADownState: TSpinButtonState);
|
|
var
|
|
R, RSrc: TRect;
|
|
dRect: Integer;
|
|
{Temp: TBitmap;}
|
|
begin
|
|
ABitmap.Height := Height;
|
|
ABitmap.Width := Width;
|
|
with ABitmap.Canvas do begin
|
|
R := Bounds(0, 0, Width, Height);
|
|
Pen.Width := 1;
|
|
Brush.Color := clBtnFace;
|
|
Brush.Style := bsSolid;
|
|
FillRect(R);
|
|
{ buttons frame }
|
|
Pen.Color := clWindowFrame;
|
|
Rectangle(0, 0, Width, Height);
|
|
MoveTo(-1, Height);
|
|
LineTo(Width, -1);
|
|
{ top button }
|
|
if ADownState = sbTopDown then Pen.Color := clBtnShadow
|
|
else Pen.Color := clBtnHighlight;
|
|
MoveTo(1, Height - 4);
|
|
LineTo(1, 1);
|
|
LineTo(Width - 3, 1);
|
|
if ADownState = sbTopDown then Pen.Color := clBtnHighlight
|
|
else Pen.Color := clBtnShadow;
|
|
if ADownState <> sbTopDown then begin
|
|
MoveTo(1, Height - 3);
|
|
LineTo(Width - 2, 0);
|
|
end;
|
|
{ bottom button }
|
|
if ADownState = sbBottomDown then Pen.Color := clBtnHighlight
|
|
else Pen.Color := clBtnShadow;
|
|
MoveTo(2, Height - 2);
|
|
LineTo(Width - 2, Height - 2);
|
|
LineTo(Width - 2, 1);
|
|
if ADownState = sbBottomDown then Pen.Color := clBtnShadow
|
|
else Pen.Color := clBtnHighlight;
|
|
MoveTo(2, Height - 2);
|
|
LineTo(Width - 1, 1);
|
|
{ top glyph }
|
|
dRect := 1;
|
|
if ADownState = sbTopDown then Inc(dRect);
|
|
R := Bounds(Round((Width / 4) - (FUpBitmap.Width / 2)) + dRect,
|
|
Round((Height / 4) - (FUpBitmap.Height / 2)) + dRect, FUpBitmap.Width,
|
|
FUpBitmap.Height);
|
|
RSrc := Bounds(0, 0, FUpBitmap.Width, FUpBitmap.Height);
|
|
{
|
|
if Self.Enabled or (csDesigning in ComponentState) then
|
|
BrushCopy(R, FUpBitmap, RSrc, FUpBitmap.TransparentColor)
|
|
else begin
|
|
Temp := CreateDisabledBitmap(FUpBitmap, clBlack);
|
|
try
|
|
BrushCopy(R, Temp, RSrc, Temp.TransparentColor);
|
|
finally
|
|
Temp.Free;
|
|
end;
|
|
end;
|
|
}
|
|
//BrushCopy(R, FUpBitmap, RSrc, FUpBitmap.TransparentColor);
|
|
StretchDraw(R, FUpBitmap);
|
|
{ bottom glyph }
|
|
R := Bounds(Round((3 * Width / 4) - (FDownBitmap.Width / 2)) - 1,
|
|
Round((3 * Height / 4) - (FDownBitmap.Height / 2)) - 1,
|
|
FDownBitmap.Width, FDownBitmap.Height);
|
|
RSrc := Bounds(0, 0, FDownBitmap.Width, FDownBitmap.Height);
|
|
{
|
|
if Self.Enabled or (csDesigning in ComponentState) then
|
|
BrushCopy(R, FDownBitmap, RSrc, FDownBitmap.TransparentColor)
|
|
else begin
|
|
Temp := CreateDisabledBitmap(FDownBitmap, clBlack);
|
|
try
|
|
BrushCopy(R, Temp, RSrc, Temp.TransparentColor);
|
|
finally
|
|
Temp.Free;
|
|
end;
|
|
end;
|
|
}
|
|
//BrushCopy(R, FDownBitmap, RSrc, FDownBitmap.TransparentColor);
|
|
StretchDraw(R, FDownBitmap);
|
|
if ADownState = sbBottomDown then begin
|
|
Pen.Color := clBtnShadow;
|
|
MoveTo(3, Height - 2);
|
|
LineTo(Width - 1, 2);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinButton.CMEnabledChanged(var Message: TMessage);
|
|
begin
|
|
inherited;
|
|
FInvalidate := True;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TRxSpinButton.TopClick;
|
|
begin
|
|
if Assigned(FOnTopClick) then begin
|
|
FOnTopClick(Self);
|
|
if not (csLButtonDown in ControlState) then FDown := sbNotDown;
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinButton.BottomClick;
|
|
begin
|
|
if Assigned(FOnBottomClick) then begin
|
|
FOnBottomClick(Self);
|
|
if not (csLButtonDown in ControlState) then FDown := sbNotDown;
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinButton.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
|
X, Y: Integer);
|
|
begin
|
|
inherited MouseDown(Button, Shift, X, Y);
|
|
if (Button = mbLeft) and Enabled then begin
|
|
if (FFocusControl <> nil) and FFocusControl.TabStop and
|
|
FFocusControl.CanFocus and (GetFocus <> FFocusControl.Handle) then
|
|
FFocusControl.SetFocus;
|
|
if FDown = sbNotDown then begin
|
|
FLastDown := FDown;
|
|
if Y > (-(Height/Width) * X + Height) then begin
|
|
FDown := sbBottomDown;
|
|
BottomClick;
|
|
end
|
|
else begin
|
|
FDown := sbTopDown;
|
|
TopClick;
|
|
end;
|
|
if FLastDown <> FDown then begin
|
|
FLastDown := FDown;
|
|
Repaint;
|
|
end;
|
|
if FRepeatTimer = nil then FRepeatTimer := TTimer.Create(Self);
|
|
FRepeatTimer.OnTimer := @TimerExpired;
|
|
FRepeatTimer.Interval := InitRepeatPause;
|
|
FRepeatTimer.Enabled := True;
|
|
end;
|
|
FDragging := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinButton.MouseMove(Shift: TShiftState; X, Y: Integer);
|
|
var
|
|
NewState: TSpinButtonState;
|
|
begin
|
|
inherited MouseMove(Shift, X, Y);
|
|
if FDragging then begin
|
|
if (X >= 0) and (X <= Width) and (Y >= 0) and (Y <= Height) then begin
|
|
NewState := FDown;
|
|
if Y > (-(Width / Height) * X + Height) then begin
|
|
if (FDown <> sbBottomDown) then begin
|
|
if FLastDown = sbBottomDown then FDown := sbBottomDown
|
|
else FDown := sbNotDown;
|
|
if NewState <> FDown then Repaint;
|
|
end;
|
|
end
|
|
else begin
|
|
if (FDown <> sbTopDown) then begin
|
|
if (FLastDown = sbTopDown) then FDown := sbTopDown
|
|
else FDown := sbNotDown;
|
|
if NewState <> FDown then Repaint;
|
|
end;
|
|
end;
|
|
end else
|
|
if FDown <> sbNotDown then begin
|
|
FDown := sbNotDown;
|
|
Repaint;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinButton.MouseUp(Button: TMouseButton; Shift: TShiftState;
|
|
X, Y: Integer);
|
|
begin
|
|
inherited MouseUp(Button, Shift, X, Y);
|
|
if FDragging then begin
|
|
FDragging := False;
|
|
if (X >= 0) and (X <= Width) and (Y >= 0) and (Y <= Height) then begin
|
|
FDown := sbNotDown;
|
|
FLastDown := sbNotDown;
|
|
Repaint;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinButton.TimerExpired(Sender: TObject);
|
|
begin
|
|
FRepeatTimer.Interval := RepeatPause;
|
|
if (FDown <> sbNotDown) and MouseCapture then begin
|
|
try
|
|
if FDown = sbBottomDown then BottomClick else TopClick;
|
|
except
|
|
FRepeatTimer.Enabled := False;
|
|
raise;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function DefBtnWidth: Integer;
|
|
begin
|
|
Result := GetSystemMetrics(SM_CXVSCROLL);
|
|
if Result > 15 then Result := 15;
|
|
end;
|
|
|
|
{ TRxSpinEdit }
|
|
|
|
constructor TRxSpinEdit.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
Text := '0';
|
|
ControlStyle := ControlStyle - [csSetCaption];
|
|
FIncrement := 1.0;
|
|
FDecimal := 2;
|
|
FEditorEnabled := True;
|
|
FArrowKeys := True;
|
|
RecreateButton;
|
|
CheckButtonVisible
|
|
end;
|
|
|
|
destructor TRxSpinEdit.Destroy;
|
|
begin
|
|
Destroying;
|
|
FChanging := True;
|
|
if FButton <> nil then
|
|
FreeAndNil(FButton);
|
|
if FBtnWindow <> nil then
|
|
FreeAndNil(FBtnWindow);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.RecreateButton;
|
|
begin
|
|
if (csDestroying in ComponentState) then
|
|
Exit;
|
|
if FButton <> nil then
|
|
FreeAndNil(FButton);
|
|
|
|
if FBtnWindow <> nil then
|
|
FreeAndNil(FBtnWindow);
|
|
|
|
FBtnWindow := TWinControl.Create(Self);
|
|
// FBtnWindow.ComponentStyle:=FBtnWindow.ComponentStyle + csSubComponent;
|
|
with FBtnWindow do
|
|
begin
|
|
FreeNotification(Self);
|
|
Height := Self.Height;
|
|
Width := Self.Height;
|
|
ControlStyle := ControlStyle + [csNoDesignSelectable];
|
|
end;
|
|
|
|
if FBtnWindow <> nil then
|
|
begin
|
|
FButton := TRxSpinButton.Create(Self);
|
|
with FButton do
|
|
begin
|
|
FocusControl := Self;
|
|
OnTopClick := @UpClick;
|
|
OnBottomClick := @DownClick;
|
|
Width := FBtnWindow.Height;
|
|
Height := FBtnWindow.Height;
|
|
FreeNotification(FBtnWindow);
|
|
end;
|
|
end;
|
|
CheckButtonVisible;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.SetArrowKeys(Value: Boolean);
|
|
begin
|
|
FArrowKeys := Value;
|
|
ResizeButton;
|
|
end;
|
|
|
|
function TRxSpinEdit.GetButtonWidth: Integer;
|
|
begin
|
|
if FBtnWindow <> nil then
|
|
Result := FBtnWindow.Width
|
|
else
|
|
Result := DefBtnWidth;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.ResizeButton;
|
|
begin
|
|
if FBtnWindow <> nil then begin
|
|
FBtnWindow.Parent := Parent;
|
|
FBtnWindow.SetBounds(Width, Top, Height, Height);
|
|
if FButton <> nil then
|
|
FButton.SetBounds(0, 0, FBtnWindow.Width, FBtnWindow.Height);
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.KeyDown(var Key: Word; Shift: TShiftState);
|
|
begin
|
|
inherited KeyDown(Key, Shift);
|
|
if ArrowKeys and (Key in [VK_UP, VK_DOWN]) then begin
|
|
if Key = VK_UP then UpClick(Self)
|
|
else if Key = VK_DOWN then DownClick(Self);
|
|
Key := 0;
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.Change;
|
|
begin
|
|
if not FChanging then inherited Change;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.KeyPress(var Key: Char);
|
|
begin
|
|
if not IsValidChar(Key) then begin
|
|
Key := #0;
|
|
Beep;
|
|
end;
|
|
if Key <> #0 then begin
|
|
inherited KeyPress(Key);
|
|
if (Key = Char(VK_RETURN)) or (Key = Char(VK_ESCAPE)) then begin
|
|
{ must catch and remove this, since is actually multi-line }
|
|
GetParentForm(Self).Perform(CM_DIALOGKEY, Byte(Key), 0);
|
|
if Key = Char(VK_RETURN) then Key := #0;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TRxSpinEdit.IsValidChar(Key: Char): Boolean;
|
|
var
|
|
ValidChars: set of Char;
|
|
begin
|
|
ValidChars := ['+', '-', '0'..'9'];
|
|
if ValueType = vtFloat then begin
|
|
if Pos(DefaultFormatSettings.DecimalSeparator, Text) = 0 then
|
|
ValidChars := ValidChars + [DefaultFormatSettings.DecimalSeparator];
|
|
if Pos('E', AnsiUpperCase(Text)) = 0 then
|
|
ValidChars := ValidChars + ['e', 'E'];
|
|
end
|
|
else if ValueType = vtHex then begin
|
|
ValidChars := ValidChars + ['A'..'F', 'a'..'f'];
|
|
end;
|
|
Result := (Key in ValidChars) or (Key < #32);
|
|
if not FEditorEnabled and Result and ((Key >= #32) or
|
|
(Key = Char(VK_BACK)) or (Key = Char(VK_DELETE))) then Result := False;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.CreateParams(var Params: TCreateParams);
|
|
begin
|
|
inherited CreateParams(Params);
|
|
end;
|
|
|
|
procedure TRxSpinEdit.SetParent(AParent: TWinControl);
|
|
begin
|
|
inherited SetParent(AParent);
|
|
if FBtnWindow <> nil then begin
|
|
FBtnWindow.Parent := AParent;
|
|
FBtnWindow.AnchorToCompanion(akLeft, 0, Self);
|
|
FBtnWindow.Visible := True;
|
|
if FButton <> nil then begin
|
|
FButton.Parent := FBtnWindow;
|
|
FButton.Visible:= True;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.Notification(AComponent: TComponent; Operation: TOperation
|
|
);
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if (AComponent = FBtnWindow) and (Operation = opRemove) then begin
|
|
if FButton <> nil then
|
|
FreeAndNil(FButton);
|
|
FreeAndNil(FBtnWindow);
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.Loaded;
|
|
begin
|
|
inherited Loaded;
|
|
CheckButtonVisible;
|
|
ResizeButton;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.CMVisibleChanged(var Msg: TLMessage);
|
|
begin
|
|
inherited CMVisibleChanged(Msg);
|
|
CheckButtonVisible;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.CreateWnd;
|
|
begin
|
|
inherited CreateWnd;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.SetAlignment(Value: TAlignment);
|
|
begin
|
|
if FAlignment <> Value then begin
|
|
FAlignment := Value;
|
|
RecreateWnd(Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.LMSize(var Message: TLMSize);
|
|
begin
|
|
inherited;
|
|
ResizeButton;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.GetTextHeight(var SysHeight, aHeight: Integer);
|
|
var
|
|
DC: HDC;
|
|
SaveFont: HFont;
|
|
SysMetrics, Metrics: TTextMetric;
|
|
begin
|
|
DC := GetDC(0);
|
|
GetTextMetrics(DC, SysMetrics);
|
|
SaveFont := SelectObject(DC, Font.Handle);
|
|
GetTextMetrics(DC, Metrics);
|
|
SelectObject(DC, SaveFont);
|
|
ReleaseDC(0, DC);
|
|
SysHeight := SysMetrics.tmHeight;
|
|
Height := Metrics.tmHeight;
|
|
end;
|
|
|
|
function TRxSpinEdit.GetMinHeight: Integer;
|
|
var
|
|
I, H: Integer;
|
|
begin
|
|
GetTextHeight(I, H);
|
|
if I > H then I := H;
|
|
Result := H + (GetSystemMetrics(SM_CYBORDER) * 4) + 1;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.UpClick(Sender: TObject);
|
|
var
|
|
OldText: string;
|
|
begin
|
|
if ReadOnly then Beep
|
|
else begin
|
|
FChanging := True;
|
|
try
|
|
OldText := inherited Text;
|
|
Value := Value + FIncrement;
|
|
finally
|
|
FChanging := False;
|
|
end;
|
|
if CompareText(inherited Text, OldText) <> 0 then begin
|
|
Modified := True;
|
|
Change;
|
|
end;
|
|
if Assigned(FOnTopClick) then FOnTopClick(Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.DownClick(Sender: TObject);
|
|
var
|
|
OldText: string;
|
|
begin
|
|
if ReadOnly then Beep
|
|
else begin
|
|
FChanging := True;
|
|
try
|
|
OldText := inherited Text;
|
|
Value := Value - FIncrement;
|
|
finally
|
|
FChanging := False;
|
|
end;
|
|
if CompareText(inherited Text, OldText) <> 0 then begin
|
|
Modified := True;
|
|
Change;
|
|
end;
|
|
if Assigned(FOnBottomClick) then FOnBottomClick(Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.CMFontChanged(var Message: TLMessage);
|
|
begin
|
|
inherited;
|
|
ResizeButton;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.CheckButtonVisible;
|
|
begin
|
|
if FBtnWindow <> nil then begin
|
|
FBtnWindow.Visible := (csDesigning in ComponentState) or Visible;
|
|
if FButton <> nil then
|
|
FButton.Visible := FBtnWindow.Visible;
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.WMSetFocus(var Message: TLMSetFocus);
|
|
begin
|
|
inherited;
|
|
end;
|
|
|
|
{procedure TRxSpinEdit.CMCtl3DChanged(var Message: TLMessage);
|
|
begin
|
|
inherited;
|
|
ResizeButton;
|
|
end;}
|
|
|
|
procedure TRxSpinEdit.CMEnabledChanged(var Message: TLMessage);
|
|
begin
|
|
inherited;
|
|
if FBtnWindow <> nil then
|
|
FBtnWindow.Enabled := Enabled;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.WMPaste(var Message: TLMessage);
|
|
begin
|
|
if not FEditorEnabled or ReadOnly then Exit;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.WMCut(var Message: TLMessage);
|
|
begin
|
|
if not FEditorEnabled or ReadOnly then Exit;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.CMExit(var Message: TLMExit);
|
|
begin
|
|
inherited;
|
|
if CheckValue(Value) <> Value then SetValue(Value);
|
|
end;
|
|
|
|
procedure TRxSpinEdit.CMEnter(var Message: TLMessage);
|
|
begin
|
|
if AutoSelect and not (csLButtonDown in ControlState) then SelectAll;
|
|
inherited;
|
|
end;
|
|
|
|
function TRxSpinEdit.GetValue: Extended;
|
|
begin
|
|
try
|
|
if ValueType = vtFloat then Result := StrToFloat(Text)
|
|
else if ValueType = vtHex then Result := StrToInt('$' + Text)
|
|
else Result := StrToInt(Text);
|
|
except
|
|
if ValueType = vtFloat then Result := FMinValue
|
|
else Result := Trunc(FMinValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TRxSpinEdit.SetValue(NewValue: Extended);
|
|
begin
|
|
if ValueType = vtFloat then
|
|
Text := FloatToStrF(CheckValue(NewValue), ffFixed, 15, FDecimal)
|
|
else if ValueType = vtHex then
|
|
Text := IntToHex(Round(CheckValue(NewValue)), 1)
|
|
else
|
|
Text := IntToStr(Round(CheckValue(NewValue)));
|
|
end;
|
|
|
|
function TRxSpinEdit.GetAsInteger: Longint;
|
|
begin
|
|
Result := Trunc(GetValue);
|
|
end;
|
|
|
|
procedure TRxSpinEdit.SetAsInteger(NewValue: Longint);
|
|
begin
|
|
SetValue(NewValue);
|
|
end;
|
|
|
|
procedure TRxSpinEdit.SetValueType(NewType: TValueType);
|
|
begin
|
|
if FValueType <> NewType then begin
|
|
FValueType := NewType;
|
|
Value := GetValue;
|
|
if FValueType in [vtInteger, vtHex] then
|
|
begin
|
|
FIncrement := Round(FIncrement);
|
|
if FIncrement = 0 then FIncrement := 1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TRxSpinEdit.IsIncrementStored: Boolean;
|
|
begin
|
|
Result := FIncrement <> 1.0;
|
|
end;
|
|
|
|
function TRxSpinEdit.IsMaxStored: Boolean;
|
|
begin
|
|
Result := (MaxValue <> 0.0);
|
|
end;
|
|
|
|
function TRxSpinEdit.IsMinStored: Boolean;
|
|
begin
|
|
Result := (MinValue <> 0.0);
|
|
end;
|
|
|
|
function TRxSpinEdit.IsValueStored: Boolean;
|
|
begin
|
|
Result := (GetValue <> 0.0);
|
|
end;
|
|
|
|
procedure TRxSpinEdit.SetDecimal(NewValue: Byte);
|
|
begin
|
|
if FDecimal <> NewValue then begin
|
|
FDecimal := NewValue;
|
|
Value := GetValue;
|
|
end;
|
|
end;
|
|
|
|
function TRxSpinEdit.CheckValue(NewValue: Extended): Extended;
|
|
begin
|
|
Result := NewValue;
|
|
if (FMaxValue <> FMinValue) then begin
|
|
if NewValue < FMinValue then
|
|
Result := FMinValue
|
|
else if NewValue > FMaxValue then
|
|
Result := FMaxValue;
|
|
end;
|
|
end;
|
|
|
|
initialization
|
|
{$I rxspin.lrs}
|
|
end.
|