2808 lines
77 KiB
ObjectPascal
2808 lines
77 KiB
ObjectPascal
{-----------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is: JvSpin.PAS, released on 2002-07-04.
|
|
|
|
The Initial Developers of the Original Code are: Fedor Koshevnikov, Igor Pavluk and Serge Korolev
|
|
Copyright (c) 1997, 1998 Fedor Koshevnikov, Igor Pavluk and Serge Korolev
|
|
Copyright (c) 2001,2002 SGB Software
|
|
All Rights Reserved.
|
|
|
|
Contributor(s):
|
|
Polaris Software
|
|
boerema1
|
|
roko
|
|
remkobonte
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.sourceforge.net
|
|
|
|
Known Issues:
|
|
|
|
-----------------------------------------------------------------------------}
|
|
// $Id: JvSpin.pas 11097 2006-12-19 21:19:05Z outchy $
|
|
|
|
unit JvSpin;
|
|
|
|
{$I jvcl.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF UNITVERSIONING}
|
|
JclUnitVersioning,
|
|
{$ENDIF UNITVERSIONING}
|
|
SysUtils, Classes,
|
|
Windows,
|
|
{$IFDEF VCL}
|
|
Messages, CommCtrl,
|
|
{$ENDIF VCL}
|
|
ComCtrls, Controls, ExtCtrls, Graphics, Forms,
|
|
{$IFDEF VisualCLX}
|
|
QComboEdits, JvQExComboEdits, QComCtrlsEx,
|
|
{$ENDIF VisualCLX}
|
|
JvEdit, JvExMask, JvMaskEdit, JvComponent;
|
|
|
|
const
|
|
DefaultInitRepeatPause = 400; { pause before repeat timer (ms) }
|
|
DefaultRepeatPause = 100;
|
|
|
|
type
|
|
TSpinButtonState = (sbNotDown, sbTopDown, sbBottomDown);
|
|
|
|
TJvSpinButtonStyle = (sbsDefault, sbsClassic); // Polaris
|
|
|
|
TJvSpinButton = class(TJvGraphicControl)
|
|
private
|
|
FDown: TSpinButtonState;
|
|
FDragging: Boolean;
|
|
FUpBitmap: TBitmap; // Custom up arrow
|
|
FDownBitmap: TBitmap; // Custom down arrow
|
|
FButtonBitmaps: Pointer;
|
|
{$IFDEF JVCLThemesEnabled}
|
|
FMouseInTopBtn: Boolean;
|
|
FMouseInBottomBtn: Boolean;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
FRepeatTimer: TTimer;
|
|
FLastDown: TSpinButtonState;
|
|
FFocusControl: TWinControl;
|
|
FOnTopClick: TNotifyEvent;
|
|
FOnBottomClick: TNotifyEvent;
|
|
FButtonStyle: TJvSpinButtonStyle;
|
|
FInitRepeatPause: Integer;
|
|
FRepeatPause: Integer;
|
|
procedure SetButtonStyle(Value: TJvSpinButtonStyle);
|
|
procedure TopClick;
|
|
procedure BottomClick;
|
|
procedure GlyphChanged(Sender: TObject);
|
|
function GetDownGlyph: TBitmap;
|
|
function GetUpGlyph: TBitmap;
|
|
procedure SetDown(Value: TSpinButtonState);
|
|
procedure SetDownGlyph(Value: TBitmap);
|
|
procedure SetFocusControl(Value: TWinControl);
|
|
procedure SetUpGlyph(Value: TBitmap);
|
|
procedure TimerExpired(Sender: TObject);
|
|
{$IFDEF VCL}
|
|
procedure CMSysColorChange(var Msg: TMessage); message CM_SYSCOLORCHANGE;
|
|
{$ENDIF VCL}
|
|
protected
|
|
procedure CheckButtonBitmaps;
|
|
procedure RemoveButtonBitmaps;
|
|
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;
|
|
|
|
function MouseInBottomBtn(const P: TPoint): Boolean;
|
|
{$IFDEF JVCLThemesEnabled}
|
|
procedure MouseEnter(Control: TControl); override;
|
|
procedure MouseLeave(Control: TControl); override;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
property Down: TSpinButtonState read FDown write SetDown default sbNotDown;
|
|
published
|
|
property ButtonStyle: TJvSpinButtonStyle read FButtonStyle write SetButtonStyle default sbsDefault;
|
|
{$IFDEF VCL}
|
|
property DragCursor;
|
|
property DragKind;
|
|
property OnEndDock;
|
|
property OnStartDock;
|
|
{$ENDIF VCL}
|
|
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 InitRepeatPause: Integer read FInitRepeatPause write FInitRepeatPause default DefaultInitRepeatPause;
|
|
property RepeatPause: Integer read FRepeatPause write FRepeatPause default DefaultRepeatPause;
|
|
property ShowHint;
|
|
property ParentShowHint;
|
|
property Anchors;
|
|
property Constraints;
|
|
property OnBottomClick: TNotifyEvent read FOnBottomClick write FOnBottomClick;
|
|
property OnTopClick: TNotifyEvent read FOnTopClick write FOnTopClick;
|
|
property OnDragDrop;
|
|
property OnDragOver;
|
|
property OnEndDrag;
|
|
property OnStartDrag;
|
|
end;
|
|
|
|
{$IFDEF BCB}
|
|
TValueType = (vtInt, vtFloat, vtHex);
|
|
{$ELSE}
|
|
TValueType = (vtInteger, vtFloat, vtHex);
|
|
{$ENDIF BCB}
|
|
|
|
TSpinButtonKind = (bkStandard, bkDiagonal, bkClassic);
|
|
|
|
TJvCheckOption = (coCheckOnChange, coCheckOnExit, coCropBeyondLimit);
|
|
TJvCheckOptions = set of TJvCheckOption;
|
|
|
|
{$IFDEF VCL}
|
|
TJvCustomSpinEdit = class(TJvExCustomMaskEdit)
|
|
{$ENDIF VCL}
|
|
{$IFDEF VisualCLX}
|
|
TJvCustomSpinEdit = class(TJvExCustomComboMaskEdit)
|
|
{$ENDIF VisualCLX}
|
|
private
|
|
FShowButton: Boolean;
|
|
FCheckMaxValue: Boolean;
|
|
FCheckMinValue: Boolean;
|
|
FCheckOptions: TJvCheckOptions;
|
|
FDisplayFormat: string;
|
|
FFocused: Boolean;
|
|
FLCheckMaxValue: Boolean;
|
|
FLCheckMinValue: Boolean;
|
|
FAlignment: TAlignment;
|
|
FMinValue: Extended;
|
|
FMaxValue: Extended;
|
|
FOldValue: Extended;
|
|
FIncrement: Extended;
|
|
FDecimal: Byte;
|
|
FChanging: Boolean;
|
|
//FOldValue: Extended; // New
|
|
FEditorEnabled: Boolean;
|
|
FValueType: TValueType;
|
|
FButton: TJvSpinButton;
|
|
FBtnWindow: TWinControl;
|
|
FArrowKeys: Boolean;
|
|
FOnTopClick: TNotifyEvent;
|
|
FOnBottomClick: TNotifyEvent;
|
|
// FButtonKind: TSpinButtonKind;
|
|
FUpDown: TCustomUpDown;
|
|
FThousands: Boolean; // New
|
|
function StoreCheckMaxValue: Boolean;
|
|
function StoreCheckMinValue: Boolean;
|
|
procedure SetCheckMaxValue(NewValue: Boolean);
|
|
procedure SetCheckMinValue(NewValue: Boolean);
|
|
procedure SetMaxValue(NewValue: Extended);
|
|
procedure SetMinValue(NewValue: Extended);
|
|
|
|
function CheckDefaultRange(CheckMax: Boolean): Boolean;
|
|
procedure SetDisplayFormat(const Value: string);
|
|
function IsFormatStored: Boolean;
|
|
//function TextToValText(const AValue: string): string;
|
|
procedure SetFocused(Value: Boolean);
|
|
//procedure CheckRange(const AOption: TJvCheckOption);
|
|
|
|
//function TryGetValue(var Value: Extended): Boolean; // New
|
|
function GetAsInteger: Longint;
|
|
function GetButtonKind: TSpinButtonKind;
|
|
function GetButtonWidth: Integer;
|
|
function GetMinHeight: Integer;
|
|
function IsIncrementStored: Boolean;
|
|
function IsMaxStored: Boolean;
|
|
function IsMinStored: Boolean;
|
|
function IsValueStored: Boolean;
|
|
procedure GetTextHeight(var SysHeight, Height: Integer);
|
|
procedure ResizeButton;
|
|
procedure SetAlignment(Value: TAlignment);
|
|
procedure SetArrowKeys(Value: Boolean);
|
|
procedure SetAsInteger(NewValue: Longint);
|
|
procedure SetButtonKind(Value: TSpinButtonKind);
|
|
procedure SetDecimal(NewValue: Byte);
|
|
procedure SetEditRect;
|
|
procedure SetThousands(Value: Boolean);
|
|
procedure UpDownClick(Sender: TObject; Button: TUDBtnType);
|
|
procedure SetShowButton(Value: Boolean);
|
|
{$IFDEF VCL}
|
|
procedure CMBiDiModeChanged(var Msg: TMessage); message CM_BIDIMODECHANGED;
|
|
procedure CMCtl3DChanged(var Msg: TMessage); message CM_CTL3DCHANGED;
|
|
{$ENDIF VCL}
|
|
protected
|
|
FButtonKind: TSpinButtonKind;
|
|
procedure WMPaste(var Msg: TMessage); message WM_PASTE;
|
|
procedure WMCut(var Msg: TMessage); message WM_CUT;
|
|
procedure FocusKilled(NextWnd: THandle); override;
|
|
function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
|
|
{$IFDEF VisualCLX} const {$ENDIF} MousePos: TPoint): Boolean; override;
|
|
procedure BoundsChanged; override;
|
|
procedure EnabledChanged; override;
|
|
procedure DoEnter; override;
|
|
procedure DoExit; override;
|
|
procedure FontChanged; override;
|
|
function CheckValue(NewValue: Extended): Extended;
|
|
function CheckValueRange(NewValue: Extended; RaiseOnError: Boolean): Extended;
|
|
function GetValue: Extended; virtual; abstract;
|
|
procedure DataChanged; virtual;
|
|
procedure RecreateButton;
|
|
procedure SetValue(NewValue: Extended); virtual; abstract;
|
|
procedure SetValueType(NewType: TValueType); virtual;
|
|
|
|
function DefaultDisplayFormat: string; virtual;
|
|
property DisplayFormat: string read FDisplayFormat write SetDisplayFormat stored IsFormatStored;
|
|
// procedure DefinePropertyes(Filer: TFiler); override;
|
|
|
|
function IsValidChar(Key: Char): Boolean; virtual;
|
|
procedure Change; override;
|
|
{$IFDEF VCL}
|
|
procedure CreateParams(var Params: TCreateParams); override;
|
|
procedure CreateWnd; override;
|
|
{$ENDIF VCL}
|
|
procedure DownClick(Sender: TObject); virtual;
|
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
|
procedure KeyPress(var Key: Char); override;
|
|
procedure UpClick(Sender: TObject); virtual;
|
|
property ButtonWidth: Integer read GetButtonWidth;
|
|
public
|
|
procedure Loaded; override;
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
|
|
property AsInteger: Longint read GetAsInteger write SetAsInteger default 0;
|
|
property Text;
|
|
property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify;
|
|
property ArrowKeys: Boolean read FArrowKeys write SetArrowKeys default True;
|
|
property ButtonKind: TSpinButtonKind read FButtonKind write SetButtonKind default bkDiagonal;
|
|
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 SetMaxValue stored IsMaxStored;
|
|
property MinValue: Extended read FMinValue write SetMinValue stored IsMinStored;
|
|
property CheckOptions: TJvCheckOptions read FCheckOptions write FCheckOptions default [coCheckOnChange, coCheckOnExit, coCropBeyondLimit];
|
|
property CheckMinValue: Boolean read FCheckMinValue write SetCheckMinValue stored StoreCheckMinValue;
|
|
property CheckMaxValue: Boolean read FCheckMaxValue write SetCheckMaxValue stored StoreCheckMaxValue;
|
|
property ValueType: TValueType read FValueType write SetValueType
|
|
default {$IFDEF BCB} vtInt {$ELSE} vtInteger {$ENDIF};
|
|
property Value: Extended read GetValue write SetValue stored IsValueStored;
|
|
property Thousands: Boolean read FThousands write SetThousands default False;
|
|
property ShowButton: Boolean read FShowButton write SetShowButton default True;
|
|
property OnBottomClick: TNotifyEvent read FOnBottomClick write FOnBottomClick;
|
|
property OnTopClick: TNotifyEvent read FOnTopClick write FOnTopClick;
|
|
end;
|
|
|
|
TJvSpinEdit = class(TJvCustomSpinEdit)
|
|
protected
|
|
procedure SetValue(NewValue: Extended); override;
|
|
function GetValue: Extended; override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
published
|
|
//Polaris
|
|
//property CheckOnExit;
|
|
property CheckOptions;
|
|
property CheckMinValue;
|
|
property CheckMaxValue;
|
|
|
|
property BeepOnError;
|
|
|
|
property Align;
|
|
property Alignment;
|
|
property ArrowKeys;
|
|
property DisplayFormat;
|
|
property ButtonKind default bkDiagonal;
|
|
property Thousands;
|
|
property Decimal;
|
|
property EditorEnabled;
|
|
property Increment;
|
|
property MaxValue;
|
|
property MinValue;
|
|
property ShowButton;
|
|
property ValueType;
|
|
property Value;
|
|
property OnBottomClick;
|
|
property OnTopClick;
|
|
|
|
property AutoSelect;
|
|
property AutoSize;
|
|
property BorderStyle;
|
|
property Color;
|
|
{$IFDEF VCL}
|
|
property DragCursor;
|
|
property BiDiMode;
|
|
property DragKind;
|
|
property ParentBiDiMode;
|
|
property ImeMode;
|
|
property ImeName;
|
|
property OnEndDock;
|
|
property OnStartDock;
|
|
{$ENDIF VCL}
|
|
property DragMode;
|
|
property Enabled;
|
|
property Font;
|
|
property Anchors;
|
|
property Constraints;
|
|
property MaxLength;
|
|
property ParentColor;
|
|
property ParentFont;
|
|
property ParentShowHint;
|
|
property PopupMenu;
|
|
property ReadOnly;
|
|
property ShowHint;
|
|
property TabOrder;
|
|
property TabStop;
|
|
property Visible;
|
|
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 OnStartDrag;
|
|
property OnContextPopup;
|
|
property OnMouseWheelDown;
|
|
property OnMouseWheelUp;
|
|
property HideSelection;
|
|
{$IFDEF VCL}
|
|
{$IFDEF COMPILER6_UP}
|
|
property BevelEdges;
|
|
property BevelInner;
|
|
property BevelKind default bkNone;
|
|
property BevelOuter;
|
|
{$ENDIF COMPILER6_UP}
|
|
{$ENDIF VCL}
|
|
property ClipboardCommands;
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
const
|
|
UnitVersioning: TUnitVersionInfo = (
|
|
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvSpin.pas $';
|
|
Revision: '$Revision: 11097 $';
|
|
Date: '$Date: 2006-12-19 22:19:05 +0100 (mar., 19 déc. 2006) $';
|
|
LogPath: 'JVCL\run'
|
|
);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
implementation
|
|
|
|
uses
|
|
Consts,
|
|
JvThemes,
|
|
{$IFDEF JVCLThemesEnabled}
|
|
UxTheme,
|
|
{$IFNDEF COMPILER7_UP}
|
|
TmSchema,
|
|
{$ENDIF !COMPILER7_UP}
|
|
{$ENDIF JVCLThemesEnabled}
|
|
JvJCLUtils, JvJVCLUtils, JvConsts, JvResources, JvToolEdit, JclStrings;
|
|
|
|
{$R JvSpin.Res}
|
|
|
|
const
|
|
sSpinUpBtn = 'JvSpinUP';
|
|
sSpinDownBtn = 'JvSpinDOWN';
|
|
sSpinUpBtnPole = 'JvSpinUPPOLE';
|
|
sSpinDownBtnPole = 'JvSpinDOWNPOLE';
|
|
|
|
(*Polaris
|
|
procedure TJvSpinButton.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);
|
|
{ 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);
|
|
if ADownState = sbBottomDown then
|
|
begin
|
|
Pen.Color := clBtnShadow;
|
|
MoveTo(3, Height - 2);
|
|
LineTo(Width - 1, 2);
|
|
end;
|
|
end;
|
|
end;
|
|
*)
|
|
|
|
type
|
|
TColorArray = array [0..2] of TColor;
|
|
{$IFDEF VisualCLX}
|
|
THackedCustomForm = class(TCustomForm);
|
|
{$ENDIF VisualCLX}
|
|
|
|
TJvUpDown = class(TCustomUpDown)
|
|
private
|
|
FChanging: Boolean;
|
|
{$IFDEF VCL}
|
|
procedure ScrollMessage(var Msg: TWMVScroll);
|
|
procedure WMHScroll(var Msg: TWMHScroll); message CN_HSCROLL;
|
|
procedure WMVScroll(var Msg: TWMVScroll); message CN_VSCROLL;
|
|
{$ENDIF VCL}
|
|
{$IFDEF VisualCLX}
|
|
protected
|
|
procedure Click(Button: TUDBtnType); override;
|
|
{$ENDIF VisualCLX}
|
|
public
|
|
procedure Resize; override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
published
|
|
property OnClick;
|
|
end;
|
|
|
|
{ The face of a spin button is stored because they are a bit to complex to
|
|
calculate everytime in a Paint method. There are multiple bitmaps stored
|
|
for a single spin button, eg disable/top-down/bottom down etc.
|
|
|
|
The face bitmaps of a spin button are stored in a TSpinButtonBitmaps
|
|
object. Multiple spin buttons can use the same TSpinButtonBitmaps object.
|
|
(That is, identical spin buttons (same height, width, button kind etc.) use the
|
|
same TSpinButtonbitmaps objects) The TSpinButtonBitmaps objects are managed
|
|
by a single TSpinButtonBitmapsManager object.
|
|
}
|
|
|
|
TSpinButtonBitmapsManager = class;
|
|
|
|
TSpinButtonBitmaps = class(TObject)
|
|
private
|
|
FManager: TSpinButtonBitmapsManager;
|
|
FHeight: Integer;
|
|
FWidth: Integer;
|
|
FStyle: TJvSpinButtonStyle;
|
|
FClientCount: Integer;
|
|
|
|
FTopDownBtn: TBitmap;
|
|
FBottomDownBtn: TBitmap;
|
|
FNotDownBtn: TBitmap;
|
|
FDisabledBtn: TBitmap;
|
|
FCustomGlyphs: Boolean;
|
|
FResetOnDraw: Boolean;
|
|
{$IFDEF JVCLThemesEnabled}
|
|
FTopHotBtn: TBitmap;
|
|
FBottomHotBtn: TBitmap;
|
|
FIsThemed: Boolean;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
protected
|
|
procedure DrawAllBitmap;
|
|
procedure DrawBitmap(ABitmap: TBitmap; ADownState: TSpinButtonState; const Enabled: Boolean);
|
|
procedure PoleDrawArrows(ACanvas: TCanvas; const AState: TSpinButtonState; const Enabled: Boolean;
|
|
AUpArrow, ADownArrow: TBitmap);
|
|
procedure JvDrawArrows(ACanvas: TCanvas; const AState: TSpinButtonState; const Enabled: Boolean;
|
|
AUpArrow, ADownArrow: TBitmap);
|
|
{$IFDEF JVCLThemesEnabled}
|
|
procedure DrawAllBitmapClassicThemed;
|
|
procedure DrawAllBitmapDiagonalThemed;
|
|
procedure DrawDiagonalThemedArrows(ACanvas: TCanvas; const AState: TSpinButtonState; const Enabled: Boolean;
|
|
AUpArrow, ADownArrow: TBitmap);
|
|
{$ENDIF JVCLThemesEnabled}
|
|
procedure Reset;
|
|
|
|
function CompareWith(const AWidth, AHeight: Integer; const AStyle: TJvSpinButtonStyle;
|
|
const ACustomGlyphs: Boolean): Integer;
|
|
public
|
|
constructor Create(AManager: TSpinButtonBitmapsManager; const AWidth, AHeight: Integer;
|
|
const AStyle: TJvSpinButtonStyle; const ACustomGlyphs: Boolean); virtual;
|
|
destructor Destroy; override;
|
|
|
|
procedure AddClient;
|
|
procedure RemoveClient;
|
|
|
|
procedure Draw(ACanvas: TCanvas; const ADown: TSpinButtonState;
|
|
const AEnabled, AMouseInTopBtn, AMouseInBottomBtn: Boolean);
|
|
procedure DrawGlyphs(ACanvas: TCanvas; const AState: TSpinButtonState; const Enabled: Boolean;
|
|
AUpArrow, ADownArrow: TBitmap);
|
|
|
|
property Width: Integer read FWidth;
|
|
property Height: Integer read FHeight;
|
|
property Style: TJvSpinButtonStyle read FStyle;
|
|
property CustomGlyphs: Boolean read FCustomGlyphs;
|
|
end;
|
|
|
|
TSpinButtonBitmapsManager = class(TObject)
|
|
private
|
|
FClientCount: Integer;
|
|
FList: TList;
|
|
protected
|
|
function Find(const Width, Height: Integer; const AButtonStyle: TJvSpinButtonStyle;
|
|
const ACustomGlyphs: Boolean; var Index: Integer): Boolean;
|
|
procedure Remove(Obj: TObject);
|
|
public
|
|
constructor Create; virtual;
|
|
destructor Destroy; override;
|
|
|
|
function WantButtons(const Width, Height: Integer; const AButtonStyle: TJvSpinButtonStyle;
|
|
const ACustomGlyphs: Boolean): TSpinButtonBitmaps;
|
|
|
|
procedure AddClient;
|
|
procedure RemoveClient;
|
|
end;
|
|
|
|
var
|
|
GSpinButtonBitmapsManager: TSpinButtonBitmapsManager = nil;
|
|
|
|
//=== Local procedures =======================================================
|
|
|
|
function SpinButtonBitmapsManager: TSpinButtonBitmapsManager;
|
|
begin
|
|
if GSpinButtonBitmapsManager = nil then
|
|
GSpinButtonBitmapsManager := TSpinButtonBitmapsManager.Create;
|
|
Result := GSpinButtonBitmapsManager;
|
|
end;
|
|
|
|
function DefBtnWidth: Integer;
|
|
begin
|
|
Result := GetSystemMetrics(SM_CXVSCROLL);
|
|
if Result > 15 then
|
|
Result := 15;
|
|
end;
|
|
|
|
function RemoveThousands(const AValue: string): string;
|
|
begin
|
|
if DecimalSeparator <> ThousandSeparator then
|
|
Result := DelChars(AValue, ThousandSeparator)
|
|
else
|
|
Result := AValue;
|
|
end;
|
|
|
|
|
|
//=== { TJvCustomSpinEdit } ==================================================
|
|
|
|
procedure TJvCustomSpinEdit.Change;
|
|
var
|
|
OldText: string;
|
|
OldSelStart: Integer;
|
|
begin
|
|
{ (rb) Maybe move to CMTextChanged }
|
|
if FChanging or not HandleAllocated then
|
|
Exit;
|
|
|
|
FChanging := True;
|
|
OldSelStart := SelStart;
|
|
try
|
|
OldText := inherited Text;
|
|
try
|
|
if not (csDesigning in ComponentState) and (coCheckOnChange in CheckOptions) then
|
|
begin
|
|
CheckValueRange(Value, not (coCropBeyondLimit in CheckOptions));
|
|
SetValue(CheckValue(Value));
|
|
end;
|
|
except
|
|
SetValue(CheckValue(Value));
|
|
end;
|
|
finally
|
|
FChanging := False;
|
|
end;
|
|
|
|
SelStart := OldSelStart;
|
|
|
|
if FOldValue <> Value then
|
|
begin
|
|
// Mantis 3469: This has the advantage to be completely transparent to
|
|
// the number of decimals shown in the control
|
|
|
|
// (outchy) only shift SelStart by the difference in number of ThousandSeparator BEFORE SelStart
|
|
// do not shift if SelStart was clamped (new text length is shorter than OldSelText)
|
|
if Thousands and (SelStart = OldSelStart) then
|
|
SelStart := SelStart + StrCharCount(Copy(Text, 1, SelStart), ThousandSeparator)
|
|
- StrCharCount(Copy(OldText, 1, SelStart),ThousandSeparator);
|
|
|
|
inherited Change;
|
|
FOldValue := Value;
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.CheckDefaultRange(CheckMax: Boolean): Boolean;
|
|
begin
|
|
Result := (FMinValue <> 0) or (FMaxValue <> 0);
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.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;
|
|
}
|
|
if FCheckMinValue or FCheckMaxValue then
|
|
begin
|
|
if FCheckMinValue and (NewValue < FMinValue) then
|
|
Result := FMinValue;
|
|
if FCheckMaxValue and (NewValue > FMaxValue) then
|
|
Result := FMaxValue;
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.CheckValueRange(NewValue: Extended; RaiseOnError: Boolean): Extended;
|
|
begin
|
|
Result := CheckValue(NewValue);
|
|
if (FCheckMinValue or FCheckMaxValue) and
|
|
RaiseOnError and (Result <> NewValue) then
|
|
raise ERangeError.CreateResFmt(@RsEOutOfRangeFloat, [FMinValue, FMaxValue]);
|
|
end;
|
|
|
|
{$IFDEF VCL}
|
|
|
|
procedure TJvCustomSpinEdit.CMBiDiModeChanged(var Msg: TMessage);
|
|
begin
|
|
inherited;
|
|
ResizeButton;
|
|
SetEditRect;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.CMCtl3DChanged(var Msg: TMessage);
|
|
begin
|
|
inherited;
|
|
ResizeButton;
|
|
SetEditRect;
|
|
end;
|
|
|
|
{$ENDIF VCL}
|
|
|
|
constructor TJvCustomSpinEdit.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
|
|
FThousands := False; //new
|
|
|
|
//Polaris
|
|
FFocused := False;
|
|
FCheckOptions := [coCheckOnChange, coCheckOnExit, coCropBeyondLimit];
|
|
FLCheckMinValue := True;
|
|
FLCheckMaxValue := True;
|
|
FCheckMinValue := False;
|
|
FCheckMaxValue := False;
|
|
//Polaris
|
|
ControlStyle := ControlStyle - [csSetCaption];
|
|
FIncrement := 1.0;
|
|
FDecimal := 2;
|
|
FEditorEnabled := True;
|
|
FButtonKind := bkDiagonal;
|
|
FArrowKeys := True;
|
|
FShowButton := True;
|
|
RecreateButton;
|
|
end;
|
|
|
|
{$IFDEF VCL}
|
|
procedure TJvCustomSpinEdit.CreateParams(var Params: TCreateParams);
|
|
const
|
|
Alignments: array [Boolean, TAlignment] of DWORD =
|
|
((ES_LEFT, ES_RIGHT, ES_CENTER), (ES_RIGHT, ES_LEFT, ES_CENTER));
|
|
begin
|
|
inherited CreateParams(Params);
|
|
// Polaris:
|
|
// or ES_MULTILINE
|
|
Params.Style := Params.Style or WS_CLIPCHILDREN or
|
|
Alignments[UseRightToLeftAlignment, FAlignment];
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.CreateWnd;
|
|
begin
|
|
inherited CreateWnd;
|
|
SetEditRect;
|
|
end;
|
|
{$ENDIF VCL}
|
|
|
|
procedure TJvCustomSpinEdit.DataChanged;
|
|
var
|
|
EditFormat: string;
|
|
WasModified: Boolean;
|
|
begin
|
|
if (ValueType = vtFloat) and FFocused and (FDisplayFormat <> '') then
|
|
begin
|
|
EditFormat := '0';
|
|
if FDecimal > 0 then
|
|
EditFormat := EditFormat + '.' + MakeStr('0', FDecimal); // See Mantis 3936 about the '0' here.
|
|
{ Changing EditText sets Modified to false }
|
|
WasModified := Modified;
|
|
try
|
|
Text := FormatFloat(EditFormat, Value);
|
|
finally
|
|
Modified := WasModified;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.DefaultDisplayFormat: string;
|
|
begin
|
|
Result := ',0.##';
|
|
end;
|
|
|
|
destructor TJvCustomSpinEdit.Destroy;
|
|
begin
|
|
Destroying;
|
|
FChanging := True;
|
|
if FButton <> nil then
|
|
begin
|
|
FButton.Free;
|
|
FButton := nil;
|
|
FBtnWindow.Free;
|
|
FBtnWindow := nil;
|
|
end;
|
|
if FUpDown <> nil then
|
|
begin
|
|
FUpDown.Free;
|
|
FUpDown := nil;
|
|
end;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.BoundsChanged;
|
|
var
|
|
MinHeight: Integer;
|
|
begin
|
|
MinHeight := GetMinHeight;
|
|
{ text edit bug: if size to less than minheight, then edit ctrl does
|
|
not display the text }
|
|
if Height < MinHeight then
|
|
Height := MinHeight
|
|
else
|
|
begin
|
|
ResizeButton;
|
|
SetEditRect;
|
|
inherited BoundsChanged;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.WMCut(var Msg: TMessage);
|
|
begin
|
|
if FEditorEnabled and not ReadOnly then
|
|
inherited;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.WMPaste(var Msg: TMessage);
|
|
begin
|
|
if FEditorEnabled and not ReadOnly then
|
|
inherited;
|
|
|
|
{ Polaris code:
|
|
if not FEditorEnabled or ReadOnly then
|
|
Exit;
|
|
V := Value;
|
|
inherited;
|
|
try
|
|
StrToFloat(Text);
|
|
except
|
|
SetValue(V);
|
|
end;
|
|
}
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.DoEnter;
|
|
begin
|
|
SetFocused(True);
|
|
if AutoSelect and not (csLButtonDown in ControlState) then
|
|
SelectAll;
|
|
inherited DoEnter;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.DoExit;
|
|
begin
|
|
SetFocused(False);
|
|
try
|
|
if not (csDesigning in ComponentState) and (coCheckOnExit in CheckOptions) then
|
|
begin
|
|
CheckValueRange(Value, not (coCropBeyondLimit in CheckOptions));
|
|
SetValue(CheckValue(Value));
|
|
end;
|
|
except
|
|
SetFocused(True);
|
|
SelectAll;
|
|
if CanFocus then
|
|
SetFocus;
|
|
raise;
|
|
end;
|
|
inherited DoExit;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.FocusKilled(NextWnd: THandle);
|
|
begin
|
|
if ([coCropBeyondLimit, coCheckOnExit] <= CheckOptions) and
|
|
not (csDesigning in ComponentState) then
|
|
SetValue(CheckValue(Value));
|
|
inherited FocusKilled(NextWnd);
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
|
|
{$IFDEF VisualCLX} const {$ENDIF} MousePos: TPoint): Boolean;
|
|
begin
|
|
if WheelDelta > 0 then
|
|
UpClick(nil)
|
|
else
|
|
DownClick(nil);
|
|
Result := True;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.DownClick(Sender: TObject);
|
|
var
|
|
OldText: string;
|
|
begin
|
|
if ReadOnly then
|
|
DoBeepOnError
|
|
else
|
|
begin
|
|
FChanging := True;
|
|
try
|
|
OldText := inherited Text;
|
|
Value := Value - FIncrement;
|
|
finally
|
|
FChanging := False;
|
|
end;
|
|
if AnsiCompareText(inherited Text, OldText) <> 0 then
|
|
begin
|
|
Modified := True;
|
|
Change;
|
|
end;
|
|
if Assigned(FOnBottomClick) then
|
|
FOnBottomClick(Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.EnabledChanged;
|
|
begin
|
|
inherited EnabledChanged;
|
|
if FUpDown <> nil then
|
|
begin
|
|
FUpDown.Enabled := Enabled;
|
|
ResizeButton;
|
|
end;
|
|
if FButton <> nil then
|
|
FButton.Enabled := Enabled;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.FontChanged;
|
|
begin
|
|
inherited FontChanged;
|
|
ResizeButton;
|
|
SetEditRect;
|
|
end;
|
|
|
|
{function TJvCustomSpinEdit.TryGetValue(var Value: Extended): Boolean;
|
|
var
|
|
S: string;
|
|
begin
|
|
try
|
|
S := StringReplace(Text, ThousandSeparator, '', [rfReplaceAll]);
|
|
if ValueType = vtFloat then
|
|
Value := StrToFloat(S)
|
|
else
|
|
if ValueType = vtHex then
|
|
Value := StrToInt('$' + Text)
|
|
else
|
|
Value := StrToInt(S);
|
|
Result := True;
|
|
except
|
|
if ValueType = vtFloat then
|
|
Value := FMinValue
|
|
else
|
|
Value := Trunc(FMinValue);
|
|
Result := False;
|
|
end;
|
|
end;}
|
|
|
|
function TJvCustomSpinEdit.GetAsInteger: Longint;
|
|
begin
|
|
Result := Trunc(GetValue);
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.GetButtonKind: TSpinButtonKind;
|
|
begin
|
|
if NewStyleControls then
|
|
Result := FButtonKind
|
|
//>Polaris
|
|
else
|
|
begin
|
|
Result := bkDiagonal;
|
|
if Assigned(FButton) and (FButton.ButtonStyle = sbsClassic) then
|
|
Result := bkClassic;
|
|
end;
|
|
//<Polaris
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.GetButtonWidth: Integer;
|
|
begin
|
|
if ShowButton then
|
|
begin
|
|
if FUpDown <> nil then
|
|
Result := FUpDown.Width
|
|
else
|
|
if FButton <> nil then
|
|
Result := FButton.Width
|
|
else
|
|
Result := DefBtnWidth;
|
|
end
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.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 TJvCustomSpinEdit.GetTextHeight(var SysHeight, Height: Integer);
|
|
var
|
|
DC: HDC;
|
|
SaveFont: HFONT;
|
|
SysMetrics, Metrics: TTextMetric;
|
|
begin
|
|
DC := GetDC(HWND_DESKTOP);
|
|
GetTextMetrics(DC, SysMetrics);
|
|
SaveFont := SelectObject(DC, Font.Handle);
|
|
GetTextMetrics(DC, Metrics);
|
|
SelectObject(DC, SaveFont);
|
|
ReleaseDC(HWND_DESKTOP, DC);
|
|
SysHeight := SysMetrics.tmHeight;
|
|
Height := Metrics.tmHeight;
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.IsFormatStored: Boolean;
|
|
begin
|
|
Result := DisplayFormat <> DefaultDisplayFormat;
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.IsIncrementStored: Boolean;
|
|
begin
|
|
Result := FIncrement <> 1.0;
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.IsMaxStored: Boolean;
|
|
begin
|
|
Result := MaxValue <> 0.0;
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.IsMinStored: Boolean;
|
|
begin
|
|
Result := MinValue <> 0.0;
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.IsValidChar(Key: Char): Boolean;
|
|
var
|
|
ValidChars: set of Char;
|
|
begin
|
|
ValidChars := DigitChars + ['+', '-'];
|
|
if ValueType = vtFloat then
|
|
begin
|
|
if Pos(DecimalSeparator, Text) = 0 then
|
|
begin
|
|
if not Thousands or (ThousandSeparator <> '.') then
|
|
ValidChars := ValidChars + [DecimalSeparator, '.'] // Polaris
|
|
else
|
|
ValidChars := ValidChars + [DecimalSeparator];
|
|
end;
|
|
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 = BackSpace) or (Key = Del)) then
|
|
Result := False;
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.IsValueStored: Boolean;
|
|
begin
|
|
Result := GetValue <> 0.0;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.KeyDown(var Key: Word; Shift: TShiftState);
|
|
begin
|
|
inherited KeyDown(Key, Shift);
|
|
if ArrowKeys and ((Key = VK_UP) or (Key = VK_DOWN)) then
|
|
begin
|
|
if Key = VK_UP then
|
|
UpClick(Self)
|
|
else
|
|
if Key = VK_DOWN then
|
|
DownClick(Self);
|
|
Key := 0;
|
|
end;
|
|
// do not delete the decimal separator while typing
|
|
// all decimal digits were moved to the integer part and new decimals were added at the end
|
|
if (Key = VK_DELETE) and (SelStart < Length(Text)) and (Text[SelStart + 1] = DecimalSeparator) then
|
|
Key := VK_RIGHT;
|
|
if (Key = VK_BACK) and (SelStart > 0) and (Text[SelStart] = DecimalSeparator) then
|
|
Key := VK_LEFT;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.KeyPress(var Key: Char);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
//Polaris
|
|
// (outchy) moved at the begining, hittinh '.' now behaves like hitting the decimal separator
|
|
if (Key = '.') and (not Thousands or (ThousandSeparator <> '.')) then
|
|
Key := DecimalSeparator;
|
|
|
|
// andreas
|
|
if (Key = DecimalSeparator) and (ValueType = vtFloat) then
|
|
begin
|
|
{ If the key is the decimal separator move the caret behind it. }
|
|
I := Pos(DecimalSeparator, Text);
|
|
if I <> 0 then
|
|
begin
|
|
Key := #0;
|
|
SelLength := 0;
|
|
SelStart := I;
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
if not IsValidChar(Key) then
|
|
begin
|
|
Key := #0;
|
|
DoBeepOnError;
|
|
end;
|
|
|
|
if Key <> #0 then
|
|
begin
|
|
inherited KeyPress(Key);
|
|
if (Key = Cr) or (Key = Esc) then
|
|
begin
|
|
{ must catch and remove this, since is actually multi-line }
|
|
{$IFDEF VCL}
|
|
GetParentForm(Self).Perform(CM_DIALOGKEY, Byte(Key), 0);
|
|
{$ENDIF VCL}
|
|
{$IFDEF VisualCLX}
|
|
THackedCustomForm(GetParentForm(Self)).WantKey(Integer(Key), [], Key);
|
|
{$ENDIF VisualCLX}
|
|
if Key = Cr then
|
|
Key := #0;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.Loaded;
|
|
begin
|
|
inherited Loaded;
|
|
FLCheckMinValue := True;
|
|
FLCheckMaxValue := True;
|
|
FOldValue := Value;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.RecreateButton;
|
|
begin
|
|
if csDestroying in ComponentState then
|
|
Exit;
|
|
FButton.Free;
|
|
FButton := nil;
|
|
FBtnWindow.Free;
|
|
FBtnWindow := nil;
|
|
FUpDown.Free;
|
|
FUpDown := nil;
|
|
if ShowButton then
|
|
if GetButtonKind = bkStandard then
|
|
begin
|
|
FUpDown := TJvUpDown.Create(Self);
|
|
with TJvUpDown(FUpDown) do
|
|
begin
|
|
Visible := True;
|
|
//Polaris
|
|
SetBounds(0, 1, DefBtnWidth, Self.Height);
|
|
if BiDiMode = bdRightToLeft then
|
|
Align := alLeft
|
|
else
|
|
Align := alRight;
|
|
{$IFDEF VCL}
|
|
Parent := Self;
|
|
{$ENDIF VCL}
|
|
{$IFDEF VisualCLX}
|
|
Parent := Self.ClientArea;
|
|
{$ENDIF VisualCLX}
|
|
OnClick := UpDownClick;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
FBtnWindow := TWinControl.Create(Self);
|
|
FBtnWindow.Visible := True;
|
|
{$IFDEF VCL}
|
|
FBtnWindow.Parent := Self;
|
|
{$ENDIF VCL}
|
|
{$IFDEF VisualCLX}
|
|
FBtnWindow.Parent := Self.ClientArea;
|
|
{$ENDIF VisualCLX}
|
|
if FButtonKind <> bkClassic then
|
|
FBtnWindow.SetBounds(0, 0, DefBtnWidth, Height)
|
|
else
|
|
FBtnWindow.SetBounds(0, 0, Height, Height);
|
|
{$IFDEF VisualCLX}
|
|
FBtnWindow.Align := alRight;
|
|
{$ENDIF VisualCLX}
|
|
FButton := TJvSpinButton.Create(Self);
|
|
FButton.Visible := True;
|
|
if FButtonKind = bkClassic then
|
|
FButton.FButtonStyle := sbsClassic;
|
|
FButton.Parent := FBtnWindow;
|
|
FButton.FocusControl := Self;
|
|
FButton.OnTopClick := UpClick;
|
|
FButton.OnBottomClick := DownClick;
|
|
//Polaris
|
|
FButton.SetBounds(1, 1, FBtnWindow.Width - 1, FBtnWindow.Height - 1);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.ResizeButton;
|
|
var
|
|
R: TRect;
|
|
begin
|
|
if FUpDown <> nil then
|
|
begin
|
|
FUpDown.Width := DefBtnWidth;
|
|
if BiDiMode = bdRightToLeft then
|
|
FUpDown.Align := alLeft
|
|
else
|
|
FUpDown.Align := alRight;
|
|
end
|
|
else
|
|
if FButton <> nil then
|
|
begin { bkDiagonal }
|
|
if NewStyleControls and {$IFDEF VCL} Ctl3D and {$ENDIF} (BorderStyle = bsSingle) then
|
|
if FButtonKind = bkClassic then
|
|
R := Bounds(Width - DefBtnWidth - 4, -1, DefBtnWidth, Height - 3)
|
|
else
|
|
R := Bounds(Width - Height - 1, -1, Height - 3, Height - 3)
|
|
else
|
|
if FButtonKind = bkClassic then
|
|
R := Bounds(Width - DefBtnWidth, 0, DefBtnWidth, Height)
|
|
else
|
|
R := Bounds(Width - Height, 0, Height, Height);
|
|
if BiDiMode = bdRightToLeft then
|
|
begin
|
|
if NewStyleControls and {$IFDEF VCL} Ctl3D and {$ENDIF} (BorderStyle = bsSingle) then
|
|
begin
|
|
R.Left := -1;
|
|
R.Right := Height - 4;
|
|
end
|
|
else
|
|
begin
|
|
R.Left := 0;
|
|
R.Right := Height;
|
|
end;
|
|
end;
|
|
with R do
|
|
FBtnWindow.SetBounds(Left, Top, Right - Left, Bottom - Top);
|
|
{$IFDEF VisualCLX}
|
|
if BiDiMode = bdRightToLeft then
|
|
FBtnWindow.Align := alLeft
|
|
else
|
|
FBtnWindow.Align := alRight;
|
|
{$ENDIF VisualCLX}
|
|
//Polaris
|
|
FButton.SetBounds(1, 1, FBtnWindow.Width - 1, FBtnWindow.Height - 1);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetAlignment(Value: TAlignment);
|
|
begin
|
|
if FAlignment <> Value then
|
|
begin
|
|
FAlignment := Value;
|
|
{$IFDEF VCL}
|
|
RecreateWnd;
|
|
{$ENDIF VCL}
|
|
{$IFDEF VisualCLX}
|
|
Invalidate;
|
|
{$ENDIF VisualCLX}
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetArrowKeys(Value: Boolean);
|
|
begin
|
|
FArrowKeys := Value;
|
|
ResizeButton;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetAsInteger(NewValue: Longint);
|
|
begin
|
|
SetValue(NewValue);
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetButtonKind(Value: TSpinButtonKind);
|
|
var
|
|
OldKind: TSpinButtonKind;
|
|
begin
|
|
OldKind := FButtonKind;
|
|
FButtonKind := Value;
|
|
if OldKind <> GetButtonKind then
|
|
begin
|
|
RecreateButton;
|
|
ResizeButton;
|
|
SetEditRect;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetCheckMaxValue(NewValue: Boolean);
|
|
begin
|
|
if FMaxValue <> 0 then
|
|
NewValue := True;
|
|
FCheckMaxValue := NewValue;
|
|
if csLoading in ComponentState then
|
|
FLCheckMaxValue := False;
|
|
SetValue(Value);
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetCheckMinValue(NewValue: Boolean);
|
|
begin
|
|
if FMinValue <> 0 then
|
|
NewValue := True;
|
|
FCheckMinValue := NewValue;
|
|
if csLoading in ComponentState then
|
|
FLCheckMinValue := False;
|
|
SetValue(Value);
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetShowButton(Value: Boolean);
|
|
begin
|
|
if FShowButton <> Value then
|
|
begin
|
|
FShowButton := Value;
|
|
RecreateButton;
|
|
ResizeButton;
|
|
SetEditRect;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetDecimal(NewValue: Byte);
|
|
begin
|
|
if FDecimal <> NewValue then
|
|
begin
|
|
FDecimal := NewValue;
|
|
Value := GetValue;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetDisplayFormat(const Value: string);
|
|
begin
|
|
if DisplayFormat <> Value then
|
|
begin
|
|
FDisplayFormat := Value;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetEditRect;
|
|
var
|
|
Loc: TRect;
|
|
begin
|
|
//Polaris
|
|
if BiDiMode = bdRightToLeft then
|
|
begin
|
|
SetRect(Loc, GetButtonWidth + 1, 0, ClientWidth - 1, ClientHeight + 1);
|
|
{$IFDEF VCL}
|
|
SendMessage(Handle, EM_SETMARGINS, EC_LEFTMARGIN, MakeLong(GetButtonWidth, 0));
|
|
{$ENDIF VCL}
|
|
end
|
|
else
|
|
begin
|
|
SetRect(Loc, 0, 0, ClientWidth - GetButtonWidth - 2, ClientHeight + 1);
|
|
{$IFDEF VCL}
|
|
SendMessage(Handle, EM_SETMARGINS, EC_RIGHTMARGIN, MakeLong(0, GetButtonWidth));
|
|
{$ENDIF VCL}
|
|
end;
|
|
{$IFDEF VCL}
|
|
SendMessage(Handle, EM_SETRECTNP, 0, Longint(@Loc));
|
|
{$ENDIF VCL}
|
|
{$IFDEF VisualCLX}
|
|
SetEditorRect(@Loc);
|
|
{$ENDIF VisualCLX}
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetFocused(Value: Boolean);
|
|
begin
|
|
if Value <> FFocused then
|
|
begin
|
|
FFocused := Value;
|
|
Invalidate;
|
|
DataChanged;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetMaxValue(NewValue: Extended);
|
|
var
|
|
Z: Boolean;
|
|
b: Boolean;
|
|
begin
|
|
if NewValue <> FMaxValue then
|
|
begin
|
|
b := not StoreCheckMaxValue;
|
|
Z := (FMaxValue = 0) <> (NewValue = 0);
|
|
FMaxValue := NewValue;
|
|
if Z and FLCheckMaxValue then
|
|
begin
|
|
SetCheckMaxValue(CheckDefaultRange(True));
|
|
if b and FLCheckMinValue then
|
|
SetCheckMinValue(CheckDefaultRange(False));
|
|
end;
|
|
SetValue(Value);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetMinValue(NewValue: Extended);
|
|
var
|
|
Z: Boolean;
|
|
b: Boolean;
|
|
begin
|
|
if NewValue <> FMinValue then
|
|
begin
|
|
b := not StoreCheckMinValue;
|
|
Z := (FMinValue = 0) <> (NewValue = 0);
|
|
FMinValue := NewValue;
|
|
if Z and FLCheckMinValue then
|
|
begin
|
|
SetCheckMinValue(CheckDefaultRange(False));
|
|
if b and FLCheckMaxValue then
|
|
SetCheckMaxValue(CheckDefaultRange(True));
|
|
end;
|
|
SetValue(Value);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetThousands(Value: Boolean);
|
|
begin
|
|
if ValueType <> vtHex then
|
|
FThousands := Value;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.SetValueType(NewType: TValueType);
|
|
begin
|
|
if FValueType <> NewType then
|
|
begin
|
|
FValueType := NewType;
|
|
Value := GetValue;
|
|
if FValueType in [{$IFDEF BCB} vtInt {$ELSE} vtInteger {$ENDIF}, vtHex] then
|
|
begin
|
|
FIncrement := Round(FIncrement);
|
|
if FIncrement = 0 then
|
|
FIncrement := 1;
|
|
end;
|
|
if FValueType = vtHex then
|
|
Thousands := False;
|
|
end;
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.StoreCheckMaxValue: Boolean;
|
|
begin
|
|
Result := (FMaxValue = 0) and (FCheckMaxValue = (FMinValue = 0));
|
|
end;
|
|
|
|
function TJvCustomSpinEdit.StoreCheckMinValue: Boolean;
|
|
begin
|
|
Result := (FMinValue = 0) and (FCheckMinValue = (FMaxValue = 0));
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.UpClick(Sender: TObject);
|
|
var
|
|
OldText: string;
|
|
begin
|
|
if ReadOnly then
|
|
DoBeepOnError
|
|
else
|
|
begin
|
|
FChanging := True;
|
|
try
|
|
OldText := inherited Text;
|
|
Value := Value + FIncrement;
|
|
finally
|
|
FChanging := False;
|
|
end;
|
|
if AnsiCompareText(inherited Text, OldText) <> 0 then
|
|
begin
|
|
Modified := True;
|
|
Change;
|
|
end;
|
|
if Assigned(FOnTopClick) then
|
|
FOnTopClick(Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvCustomSpinEdit.UpDownClick(Sender: TObject; Button: TUDBtnType);
|
|
begin
|
|
if TabStop and CanFocus then
|
|
SetFocus;
|
|
case Button of
|
|
btNext:
|
|
UpClick(Sender);
|
|
btPrev:
|
|
DownClick(Sender);
|
|
end;
|
|
end;
|
|
|
|
//=== { TJvSpinButton } ======================================================
|
|
|
|
procedure TJvSpinButton.BottomClick;
|
|
begin
|
|
if Assigned(FOnBottomClick) then
|
|
begin
|
|
FOnBottomClick(Self);
|
|
if not (csLButtonDown in ControlState) then
|
|
FDown := sbNotDown;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvSpinButton.CheckButtonBitmaps;
|
|
begin
|
|
if Assigned(FButtonBitmaps) and
|
|
((TSpinButtonBitmaps(FButtonBitmaps).Height <> Height) or
|
|
(TSpinButtonBitmaps(FButtonBitmaps).Width <> Width)) then
|
|
RemoveButtonBitmaps;
|
|
|
|
if FButtonBitmaps = nil then
|
|
begin
|
|
FButtonBitmaps := SpinButtonBitmapsManager.WantButtons(Width, Height, ButtonStyle,
|
|
not FUpBitmap.Empty or not FDownBitmap.Empty);
|
|
TSpinButtonBitmaps(FButtonBitmaps).AddClient;
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF VCL}
|
|
procedure TJvSpinButton.CMSysColorChange(var Msg: TMessage);
|
|
begin
|
|
// The buttons we draw are buffered, thus we need to repaint them to theme changes etc.
|
|
if FButtonBitmaps <> nil then
|
|
TSpinButtonBitmaps(FButtonBitmaps).Reset;
|
|
end;
|
|
{$ENDIF VCL}
|
|
|
|
constructor TJvSpinButton.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FButtonStyle := sbsDefault;
|
|
FUpBitmap := TBitmap.Create;
|
|
FDownBitmap := TBitmap.Create;
|
|
FUpBitmap.OnChange := GlyphChanged;
|
|
FDownBitmap.OnChange := GlyphChanged;
|
|
Height := 20;
|
|
Width := 20;
|
|
FLastDown := sbNotDown;
|
|
FButtonBitmaps := nil;
|
|
FInitRepeatPause := DefaultInitRepeatPause;
|
|
FRepeatPause := DefaultRepeatPause;
|
|
|
|
SpinButtonBitmapsManager.AddClient;
|
|
end;
|
|
|
|
destructor TJvSpinButton.Destroy;
|
|
begin
|
|
RemoveButtonBitmaps;
|
|
SpinButtonBitmapsManager.RemoveClient;
|
|
|
|
FUpBitmap.Free;
|
|
FDownBitmap.Free;
|
|
FRepeatTimer.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TJvSpinButton.GetDownGlyph: TBitmap;
|
|
begin
|
|
Result := FDownBitmap;
|
|
end;
|
|
|
|
function TJvSpinButton.GetUpGlyph: TBitmap;
|
|
begin
|
|
Result := FUpBitmap;
|
|
end;
|
|
|
|
procedure TJvSpinButton.GlyphChanged(Sender: TObject);
|
|
begin
|
|
if Sender is TBitmap then
|
|
(Sender as TBitmap).Transparent := True;
|
|
RemoveButtonBitmaps;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TJvSpinButton.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;
|
|
//>Polaris
|
|
if ((FButtonStyle = sbsDefault) and (Y > (-(Height / Width) * X + Height))) or
|
|
((FButtonStyle = sbsClassic) and (Y > (Height div 2))) 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;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
procedure TJvSpinButton.MouseEnter(Control: TControl);
|
|
begin
|
|
if csDesigning in ComponentState then
|
|
Exit;
|
|
{ (rb) only themed spin buttons have hot states, so it's not necessairy
|
|
to calc FMouseInBottomBtn and FMouseInTopBtn for non-themed apps }
|
|
if not FMouseInTopBtn and not FMouseInBottomBtn then
|
|
begin
|
|
if MouseInBottomBtn(ScreenToClient(Mouse.CursorPos)) then
|
|
FMouseInBottomBtn := True
|
|
else
|
|
FMouseInTopBtn := True;
|
|
if ThemeServices.ThemesEnabled then
|
|
Repaint;
|
|
inherited MouseEnter(Control);
|
|
end;
|
|
end;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
function TJvSpinButton.MouseInBottomBtn(const P: TPoint): Boolean;
|
|
begin
|
|
with P do
|
|
Result :=
|
|
((FButtonStyle = sbsDefault)) and (Y > (-(Width / Height) * X + Height)) or
|
|
((FButtonStyle = sbsClassic) and (Y > (Height div 2)));
|
|
end;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
procedure TJvSpinButton.MouseLeave(Control: TControl);
|
|
begin
|
|
if csDesigning in ComponentState then
|
|
Exit;
|
|
if FMouseInTopBtn or FMouseInBottomBtn then
|
|
begin
|
|
FMouseInTopBtn := False;
|
|
FMouseInBottomBtn := False;
|
|
if ThemeServices.ThemesEnabled then
|
|
Repaint;
|
|
inherited MouseLeave(Control);
|
|
end;
|
|
end;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
procedure TJvSpinButton.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;
|
|
//>Polaris
|
|
if MouseInBottomBtn(Point(X, Y)) 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
|
|
{$IFDEF JVCLThemesEnabled}
|
|
else
|
|
if (FMouseInTopBtn or FMouseInBottomBtn) and ThemeServices.ThemesEnabled then
|
|
begin
|
|
if MouseInBottomBtn(Point(X, Y)) then
|
|
begin
|
|
if not FMouseInBottomBtn then
|
|
begin
|
|
FMouseInTopBtn := False;
|
|
FMouseInBottomBtn := True;
|
|
Repaint;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if not FMouseInTopBtn then
|
|
begin
|
|
FMouseInTopBtn := True;
|
|
FMouseInBottomBtn := False;
|
|
Repaint;
|
|
end;
|
|
end;
|
|
end;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
end;
|
|
|
|
procedure TJvSpinButton.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 TJvSpinButton.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if (Operation = opRemove) and (AComponent = FFocusControl) then
|
|
FFocusControl := nil;
|
|
end;
|
|
|
|
procedure TJvSpinButton.Paint;
|
|
begin
|
|
CheckButtonBitmaps;
|
|
|
|
if not Enabled and not (csDesigning in ComponentState) then
|
|
FDragging := False;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
TSpinButtonBitmaps(FButtonBitmaps).Draw(Canvas, FDown, Enabled, FMouseInTopBtn, FMouseInBottomBtn);
|
|
{$ELSE}
|
|
TSpinButtonBitmaps(FButtonBitmaps).Draw(Canvas, FDown, Enabled, False, False);
|
|
{$ENDIF JVCLThemesEnabled}
|
|
if not FUpBitmap.Empty or not FDownBitmap.Empty then
|
|
TSpinButtonBitmaps(FButtonBitmaps).DrawGlyphs(Canvas, FDown, Enabled, FUpBitmap, FDownBitmap);
|
|
end;
|
|
|
|
procedure TJvSpinButton.RemoveButtonBitmaps;
|
|
begin
|
|
if Assigned(FButtonBitmaps) then
|
|
begin
|
|
TSpinButtonBitmaps(FButtonBitmaps).RemoveClient;
|
|
FButtonBitmaps := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvSpinButton.SetButtonStyle(Value: TJvSpinButtonStyle);
|
|
begin
|
|
if Value <> FButtonStyle then
|
|
begin
|
|
FButtonStyle := Value;
|
|
GlyphChanged(Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvSpinButton.SetDown(Value: TSpinButtonState);
|
|
var
|
|
OldState: TSpinButtonState;
|
|
begin
|
|
OldState := FDown;
|
|
FDown := Value;
|
|
if OldState <> FDown then
|
|
Repaint;
|
|
end;
|
|
|
|
procedure TJvSpinButton.SetDownGlyph(Value: TBitmap);
|
|
begin
|
|
if Value <> nil then
|
|
FDownBitmap.Assign(Value)
|
|
else
|
|
FDownBitmap.Handle := NullHandle;
|
|
end;
|
|
|
|
procedure TJvSpinButton.SetFocusControl(Value: TWinControl);
|
|
begin
|
|
FFocusControl := Value;
|
|
if Value <> nil then
|
|
Value.FreeNotification(Self);
|
|
end;
|
|
|
|
procedure TJvSpinButton.SetUpGlyph(Value: TBitmap);
|
|
begin
|
|
if Value <> nil then
|
|
FUpBitmap.Assign(Value)
|
|
else
|
|
FUpBitmap.Handle := NullHandle;
|
|
end;
|
|
|
|
procedure TJvSpinButton.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;
|
|
|
|
procedure TJvSpinButton.TopClick;
|
|
begin
|
|
if Assigned(FOnTopClick) then
|
|
begin
|
|
FOnTopClick(Self);
|
|
if not (csLButtonDown in ControlState) then
|
|
FDown := sbNotDown;
|
|
end;
|
|
end;
|
|
|
|
//=== { TJvSpinEdit } ========================================================
|
|
|
|
// (rom) quite unusual not to have it in the Custom base class
|
|
|
|
constructor TJvSpinEdit.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
Text := '0';
|
|
end;
|
|
|
|
function TJvSpinEdit.GetValue: Extended;
|
|
begin
|
|
try
|
|
case ValueType of
|
|
vtFloat:
|
|
begin
|
|
if FDisplayFormat <> '' then
|
|
try
|
|
Result := StrToFloat(TextToValText(Text));
|
|
except
|
|
Result := FMinValue;
|
|
end
|
|
else
|
|
if not TextToFloat(PChar(RemoveThousands(Text)), Result, fvExtended) then
|
|
Result := FMinValue;
|
|
end;
|
|
vtHex:
|
|
Result := StrToIntDef('$' + Text, Round(FMinValue));
|
|
else {vtInteger}
|
|
Result := StrToIntDef(RemoveThousands(Text), Round(FMinValue));
|
|
end;
|
|
except
|
|
if ValueType = vtFloat then
|
|
Result := FMinValue
|
|
else
|
|
Result := Round(FMinValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvSpinEdit.SetValue(NewValue: Extended);
|
|
var
|
|
FloatFormat: TFloatFormat;
|
|
WasModified: Boolean;
|
|
begin
|
|
if Thousands then
|
|
FloatFormat := ffNumber
|
|
else
|
|
FloatFormat := ffFixed;
|
|
|
|
{ Changing EditText sets Modified to false }
|
|
WasModified := Modified;
|
|
try
|
|
case ValueType of
|
|
vtFloat:
|
|
if FDisplayFormat <> '' then
|
|
Text := FormatFloat(FDisplayFormat, CheckValue(NewValue))
|
|
else
|
|
Text := FloatToStrF(CheckValue(NewValue), FloatFormat, 15, FDecimal);
|
|
vtHex:
|
|
if ValueType = vtHex then
|
|
Text := IntToHex(Round(CheckValue(NewValue)), 1);
|
|
else {vtInteger}
|
|
//Text := IntToStr(Round(CheckValue(NewValue)));
|
|
Text := FloatToStrF(CheckValue(NewValue), FloatFormat, 15, 0);
|
|
end;
|
|
DataChanged;
|
|
finally
|
|
Modified := WasModified;
|
|
end;
|
|
end;
|
|
|
|
//=== { TJvUpDown } ==========================================================
|
|
|
|
constructor TJvUpDown.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
Orientation := udVertical;
|
|
Min := -1;
|
|
Max := 1;
|
|
Position := 0;
|
|
end;
|
|
|
|
destructor TJvUpDown.Destroy;
|
|
begin
|
|
OnClick := nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TJvUpDown.Resize;
|
|
begin
|
|
if Width <> DefBtnWidth then
|
|
Width := DefBtnWidth
|
|
else
|
|
inherited Resize;
|
|
end;
|
|
|
|
{$IFDEF VCL}
|
|
|
|
procedure TJvUpDown.ScrollMessage(var Msg: TWMVScroll);
|
|
begin
|
|
if Msg.ScrollCode = SB_THUMBPOSITION then
|
|
begin
|
|
if not FChanging then
|
|
begin
|
|
FChanging := True;
|
|
try
|
|
if Msg.Pos > 0 then
|
|
Click(btNext)
|
|
else
|
|
if Msg.Pos < 0 then
|
|
Click(btPrev);
|
|
if HandleAllocated then
|
|
SendMessage(Handle, UDM_SETPOS, 0, 0);
|
|
finally
|
|
FChanging := False;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvUpDown.WMHScroll(var Msg: TWMHScroll);
|
|
begin
|
|
ScrollMessage(TWMVScroll(Msg));
|
|
end;
|
|
|
|
procedure TJvUpDown.WMVScroll(var Msg: TWMVScroll);
|
|
begin
|
|
ScrollMessage(Msg);
|
|
end;
|
|
|
|
{$ENDIF VCL}
|
|
|
|
{$IFDEF VisualCLX}
|
|
procedure TJvUpDown.Click(Button: TUDBtnType);
|
|
var
|
|
Pos: Integer;
|
|
begin
|
|
if not FChanging then
|
|
begin
|
|
FChanging := True;
|
|
try
|
|
Pos := Position;
|
|
UpdatePosition(0);
|
|
finally
|
|
FChanging := False;
|
|
end;
|
|
if Pos < 0 then
|
|
inherited Click(btPrev)
|
|
else
|
|
inherited Click(btNext)
|
|
end;
|
|
end;
|
|
{$ENDIF VisualCLX}
|
|
|
|
//=== { TSpinButtonBitmaps } =================================================
|
|
|
|
procedure TSpinButtonBitmaps.AddClient;
|
|
begin
|
|
Inc(FClientCount);
|
|
end;
|
|
|
|
function TSpinButtonBitmaps.CompareWith(const AWidth, AHeight: Integer;
|
|
const AStyle: TJvSpinButtonStyle; const ACustomGlyphs: Boolean): Integer;
|
|
begin
|
|
// used by the binary search
|
|
Result := Self.Width - AWidth;
|
|
if Result = 0 then
|
|
begin
|
|
Result := Self.Height - AHeight;
|
|
if Result = 0 then
|
|
begin
|
|
Result := Ord(Self.Style) - Ord(AStyle);
|
|
if Result = 0 then
|
|
Result := Ord(Self.CustomGlyphs) - Ord(ACustomGlyphs);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
constructor TSpinButtonBitmaps.Create(AManager: TSpinButtonBitmapsManager;
|
|
const AWidth, AHeight: Integer; const AStyle: TJvSpinButtonStyle; const ACustomGlyphs: Boolean);
|
|
begin
|
|
inherited Create;
|
|
FManager := AManager;
|
|
FWidth := AWidth;
|
|
FHeight := AHeight;
|
|
FStyle := AStyle;
|
|
FCustomGlyphs := ACustomGlyphs;
|
|
|
|
FTopDownBtn := TBitmap.Create;
|
|
FBottomDownBtn := TBitmap.Create;
|
|
FNotDownBtn := TBitmap.Create;
|
|
FDisabledBtn := TBitmap.Create;
|
|
{$IFDEF JVCLThemesEnabled}
|
|
FTopHotBtn := TBitmap.Create;
|
|
FBottomHotBtn := TBitmap.Create;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
DrawAllBitmap;
|
|
end;
|
|
|
|
destructor TSpinButtonBitmaps.Destroy;
|
|
begin
|
|
FManager.Remove(Self);
|
|
|
|
FTopDownBtn.Free;
|
|
FBottomDownBtn.Free;
|
|
FNotDownBtn.Free;
|
|
FDisabledBtn.Free;
|
|
{$IFDEF JVCLThemesEnabled}
|
|
FTopHotBtn.Free;
|
|
FBottomHotBtn.Free;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TSpinButtonBitmaps.Draw(ACanvas: TCanvas;
|
|
const ADown: TSpinButtonState; const AEnabled, AMouseInTopBtn, AMouseInBottomBtn: Boolean);
|
|
begin
|
|
if FResetOnDraw then
|
|
begin
|
|
DrawAllBitmap;
|
|
FResetOnDraw := False;
|
|
end;
|
|
|
|
with ACanvas do
|
|
if not AEnabled then
|
|
Draw(0, 0, FDisabledBtn)
|
|
else
|
|
case ADown of
|
|
sbNotDown:
|
|
{$IFDEF JVCLThemesEnabled}
|
|
if FIsThemed then
|
|
begin
|
|
if AMouseInTopBtn then
|
|
Draw(0, 0, FTopHotBtn)
|
|
else
|
|
if AMouseInBottomBtn then
|
|
Draw(0, 0, FBottomHotBtn)
|
|
else
|
|
Draw(0, 0, FNotDownBtn);
|
|
end
|
|
else
|
|
{$ENDIF JVCLThemesEnabled}
|
|
Draw(0, 0, FNotDownBtn);
|
|
sbTopDown:
|
|
Draw(0, 0, FTopDownBtn);
|
|
sbBottomDown:
|
|
Draw(0, 0, FBottomDownBtn);
|
|
end;
|
|
end;
|
|
|
|
procedure TSpinButtonBitmaps.DrawAllBitmap;
|
|
begin
|
|
{$IFDEF JVCLThemesEnabled}
|
|
FIsThemed := ThemeServices.ThemesEnabled;
|
|
if FIsThemed then
|
|
begin
|
|
if FStyle = sbsClassic then
|
|
DrawAllBitmapClassicThemed
|
|
else
|
|
DrawAllBitmapDiagonalThemed;
|
|
Exit;
|
|
end;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
DrawBitmap(FTopDownBtn, sbTopDown, True);
|
|
DrawBitmap(FBottomDownBtn, sbBottomDown, True);
|
|
DrawBitmap(FNotDownBtn, sbNotDown, True);
|
|
DrawBitmap(FDisabledBtn, sbNotDown, False);
|
|
end;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
|
|
procedure TSpinButtonBitmaps.DrawAllBitmapClassicThemed;
|
|
type
|
|
TButtonPartState = (bpsNormal, bpsHot, bpsPressed, bpsDisabled);
|
|
const
|
|
CDetails: array [Boolean, TButtonPartState] of TThemedSpin = (
|
|
(tsUpNormal, tsUpHot, tsUpPressed, tsUpDisabled),
|
|
(tsDownNormal, tsDownHot, tsDownPressed, tsDownDisabled)
|
|
);
|
|
var
|
|
TopRect, BottomRect: TRect;
|
|
TopRegion_TopAbove, BottomRegion_TopAbove: HRGN;
|
|
TopRegion_BottomAbove, BottomRegion_BottomAbove: HRGN;
|
|
|
|
procedure ConstructThemedButton(ABitmap: TBitmap; const AUpState, ADownState: TButtonPartState);
|
|
var
|
|
Details: TThemedElementDetails;
|
|
begin
|
|
with ABitmap do
|
|
begin
|
|
Height := Self.Height;
|
|
Width := Self.Width;
|
|
|
|
with Canvas do
|
|
begin
|
|
// Select only top button
|
|
if AUpState = bpsNormal then
|
|
SelectClipRgn(Handle, TopRegion_BottomAbove)
|
|
else
|
|
SelectClipRgn(Handle, TopRegion_TopAbove);
|
|
// Copy top button
|
|
Details := ThemeServices.GetElementDetails(CDetails[False, AUpState]);
|
|
ThemeServices.DrawElement(Handle, Details, TopRect);
|
|
// Select only bottom button
|
|
if AUpState = bpsNormal then
|
|
SelectClipRgn(Handle, BottomRegion_BottomAbove)
|
|
else
|
|
SelectClipRgn(Handle, BottomRegion_TopAbove);
|
|
// Copy bottom button
|
|
Details := ThemeServices.GetElementDetails(CDetails[True, ADownState]);
|
|
ThemeServices.DrawElement(Handle, Details, BottomRect);
|
|
// Remove clipping restriction
|
|
SelectClipRgn(Handle, 0);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
TopRect := Rect(0, 0, Width, Height div 2);
|
|
InflateRect(TopRect, 1, 1);
|
|
|
|
BottomRect := Rect(0, TopRect.Bottom, Width, Height);
|
|
InflateRect(BottomRect, 1, 1);
|
|
|
|
{ Construct the regions (needed because the up & down buttons overlap
|
|
each other) }
|
|
with TopRect do
|
|
begin
|
|
TopRegion_TopAbove := CreateRectRgn(Left, Top, Right, Bottom + 1);
|
|
TopRegion_BottomAbove := CreateRectRgn(Left, Top, Right, Bottom);
|
|
end;
|
|
with BottomRect do
|
|
begin
|
|
BottomRegion_TopAbove := CreateRectRgn(Left, Top + 1, Right, Bottom);
|
|
BottomRegion_BottomAbove := CreateRectRgn(Left, Top, Right, Bottom);
|
|
end;
|
|
try
|
|
{ Draw the buttons }
|
|
ConstructThemedButton(FTopDownBtn, bpsPressed, bpsNormal);
|
|
ConstructThemedButton(FBottomDownBtn, bpsNormal, bpsPressed);
|
|
ConstructThemedButton(FNotDownBtn, bpsNormal, bpsNormal);
|
|
ConstructThemedButton(FTopHotBtn, bpsHot, bpsNormal);
|
|
ConstructThemedButton(FBottomHotBtn, bpsNormal, bpsHot);
|
|
ConstructThemedButton(FDisabledBtn, bpsDisabled, bpsDisabled);
|
|
finally
|
|
DeleteObject(TopRegion_TopAbove);
|
|
DeleteObject(BottomRegion_TopAbove);
|
|
DeleteObject(TopRegion_BottomAbove);
|
|
DeleteObject(BottomRegion_BottomAbove);
|
|
end;
|
|
end;
|
|
|
|
procedure TSpinButtonBitmaps.DrawAllBitmapDiagonalThemed;
|
|
type
|
|
TButtonPartState = (bpsNormal, bpsHot, bpsPressed, bpsDisabled);
|
|
const
|
|
CDetails: array [TButtonPartState] of TThemedButton =
|
|
(tbPushButtonNormal, tbPushButtonHot, tbPushButtonPressed, tbPushButtonDisabled);
|
|
var
|
|
TemplateButtons: array [TButtonPartState] of TBitmap;
|
|
ThemeColors: array [0..2] of Cardinal;
|
|
ButtonRect: TRect;
|
|
PaintRect: TRect;
|
|
TopRegion, BottomRegion: HRGN;
|
|
UpBitmap, DownBitmap: TBitmap;
|
|
|
|
procedure ConstructThemedButton(ABitmap: TBitmap; const AUpState, ADownState: TButtonPartState);
|
|
begin
|
|
with ABitmap do
|
|
begin
|
|
Height := Self.Height;
|
|
Width := Self.Width;
|
|
|
|
with Canvas do
|
|
begin
|
|
{ Select only top button }
|
|
SelectClipRgn(Handle, TopRegion);
|
|
{ Copy top button }
|
|
ABitmap.Canvas.Draw(0, 0, TemplateButtons[AUpState]);
|
|
{ Select only bottom button }
|
|
SelectClipRgn(Handle, BottomRegion);
|
|
{ Copy bottom button }
|
|
ABitmap.Canvas.Draw(0, 0, TemplateButtons[ADownState]);
|
|
{ Remove clipping restriction }
|
|
SelectClipRgn(Handle, 0);
|
|
|
|
{ Draw diagonal }
|
|
Pen.Color := ThemeColors[0];
|
|
MoveTo(PaintRect.Left, PaintRect.Bottom - 2);
|
|
LineTo(PaintRect.Right - 1, PaintRect.Top - 1);
|
|
|
|
Pen.Color := ThemeColors[1];
|
|
MoveTo(PaintRect.Right - 1, PaintRect.Top);
|
|
LineTo(PaintRect.Right - 1, PaintRect.Top);
|
|
LineTo(PaintRect.Left, PaintRect.Bottom - 1);
|
|
|
|
Pen.Color := ThemeColors[2];
|
|
MoveTo(PaintRect.Left + 1, PaintRect.Bottom - 1);
|
|
LineTo(PaintRect.Right, PaintRect.Top);
|
|
|
|
if not CustomGlyphs then
|
|
DrawDiagonalThemedArrows(ABitmap.Canvas, sbNotDown,
|
|
AUpState <> bpsDisabled, UpBitmap, DownBitmap);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
ptButton: array [0..2] of TPoint;
|
|
State: TButtonPartState;
|
|
Details: TThemedElementDetails;
|
|
begin
|
|
TemplateButtons[bpsNormal] := TBitmap.Create;
|
|
TemplateButtons[bpsHot] := TBitmap.Create;
|
|
TemplateButtons[bpsPressed] := TBitmap.Create;
|
|
TemplateButtons[bpsDisabled] := TBitmap.Create;
|
|
try
|
|
ButtonRect := Bounds(0, 0, Width, Height);
|
|
PaintRect := ButtonRect;
|
|
InflateRect(ButtonRect, 1, 1);
|
|
InflateRect(PaintRect, -1, -1);
|
|
{ Init templates }
|
|
for State := Low(TButtonPartState) to High(TButtonPartState) do
|
|
with TemplateButtons[State] do
|
|
begin
|
|
Height := Self.Height;
|
|
Width := Self.Width;
|
|
Details := ThemeServices.GetElementDetails(CDetails[State]);
|
|
ThemeServices.DrawElement(Canvas.Handle, Details, ButtonRect);
|
|
end;
|
|
|
|
{ Init diagonal colors }
|
|
Details := ThemeServices.GetElementDetails(tbPushButtonNormal);
|
|
with Details do
|
|
begin
|
|
GetThemeColor(ThemeServices.Theme[Element], Part, State, TMT_EDGELIGHTCOLOR, ThemeColors[0]);
|
|
GetThemeColor(ThemeServices.Theme[Element], Part, State, TMT_BORDERCOLORHINT, ThemeColors[1]);
|
|
GetThemeColor(ThemeServices.Theme[Element], Part, State, TMT_EDGESHADOWCOLOR, ThemeColors[2]);
|
|
end;
|
|
|
|
UpBitmap := nil;
|
|
DownBitmap := nil;
|
|
try
|
|
if not CustomGlyphs then
|
|
begin
|
|
UpBitmap := TBitmap.Create;
|
|
UpBitmap.Handle := LoadBitmap(HInstance, sSpinUpBtn);
|
|
UpBitmap.Transparent := True;
|
|
DownBitmap := TBitmap.Create;
|
|
DownBitmap.Handle := LoadBitmap(HInstance, sSpinDownBtn);
|
|
DownBitmap.Transparent := True;
|
|
end;
|
|
|
|
{ Init regions, needed to draw the triangles }
|
|
ptButton[0] := Point(ButtonRect.Left, ButtonRect.Bottom);
|
|
ptButton[1] := Point(ButtonRect.Left, ButtonRect.Top);
|
|
ptButton[2] := Point(ButtonRect.Right, ButtonRect.Top);
|
|
TopRegion := CreatePolygonRgn(ptButton, 3, WINDING);
|
|
ptButton[0] := Point(ButtonRect.Right, ButtonRect.Top);
|
|
ptButton[1] := Point(ButtonRect.Right, ButtonRect.Bottom);
|
|
ptButton[2] := Point(ButtonRect.Left, ButtonRect.Bottom);
|
|
BottomRegion := CreatePolygonRgn(ptButton, 3, WINDING);
|
|
try
|
|
{ Draw the buttons }
|
|
ConstructThemedButton(FTopDownBtn, bpsPressed, bpsNormal);
|
|
ConstructThemedButton(FBottomDownBtn, bpsNormal, bpsPressed);
|
|
ConstructThemedButton(FNotDownBtn, bpsNormal, bpsNormal);
|
|
ConstructThemedButton(FTopHotBtn, bpsHot, bpsNormal);
|
|
ConstructThemedButton(FBottomHotBtn, bpsNormal, bpsHot);
|
|
ConstructThemedButton(FDisabledBtn, bpsDisabled, bpsDisabled);
|
|
finally
|
|
DeleteObject(TopRegion);
|
|
DeleteObject(BottomRegion);
|
|
end;
|
|
finally
|
|
UpBitmap.Free;
|
|
DownBitmap.Free;
|
|
end;
|
|
finally
|
|
TemplateButtons[bpsNormal].Free;
|
|
TemplateButtons[bpsHot].Free;
|
|
TemplateButtons[bpsPressed].Free;
|
|
TemplateButtons[bpsDisabled].Free;
|
|
end;
|
|
end;
|
|
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
procedure TSpinButtonBitmaps.DrawBitmap(ABitmap: TBitmap;
|
|
ADownState: TSpinButtonState; const Enabled: Boolean);
|
|
const
|
|
CColors: TColorArray = (clBtnShadow, clBtnHighlight, cl3DDkShadow);
|
|
var
|
|
ButtonRect: TRect;
|
|
LColors: TColorArray;
|
|
UpArrow, DownArrow: TBitmap;
|
|
|
|
procedure JvDraw;
|
|
var
|
|
TopFlags, BottomFlags: DWORD;
|
|
R: TRect;
|
|
begin
|
|
TopFlags := EDGE_RAISED;
|
|
BottomFlags := EDGE_RAISED;
|
|
R := ButtonRect;
|
|
|
|
with ABitmap.Canvas do
|
|
begin
|
|
{$IFDEF VisualCLX}
|
|
Start;
|
|
{$ENDIF VisualCLX}
|
|
LColors := CColors;
|
|
if ADownState = sbTopDown then
|
|
begin
|
|
LColors[0] := clBtnFace;
|
|
LColors[2] := clBtnHighlight;
|
|
TopFlags := EDGE_SUNKEN;
|
|
end;
|
|
if ADownState = sbBottomDown then
|
|
begin
|
|
LColors[1] := clWindowFrame;
|
|
LColors[2] := clBtnShadow;
|
|
BottomFlags := EDGE_SUNKEN;
|
|
end;
|
|
DrawEdge(Handle, R, TopFlags, BF_TOPLEFT or BF_SOFT);
|
|
DrawEdge(Handle, R, BottomFlags, BF_BOTTOMRIGHT or BF_SOFT);
|
|
InflateRect(R, -1, -1);
|
|
|
|
Pen.Color := LColors[0];
|
|
MoveTo(R.Left, R.Bottom - 2);
|
|
LineTo(R.Right - 1, R.Top - 1);
|
|
|
|
Pen.Color := LColors[2];
|
|
MoveTo(R.Right - 1, R.Top);
|
|
LineTo(R.Right - 1, R.Top);
|
|
LineTo(R.Left, R.Bottom - 1);
|
|
|
|
Pen.Color := LColors[1];
|
|
MoveTo(R.Left + 1, R.Bottom - 1);
|
|
LineTo(R.Right, R.Top);
|
|
|
|
if not CustomGlyphs then
|
|
begin
|
|
UpArrow.Assign(nil); // fixes GDI resource leak
|
|
UpArrow.LoadFromResourceName(HInstance, sSpinUpBtn);
|
|
UpArrow.TransparentColor := clWhite;
|
|
UpArrow.Transparent := True;
|
|
DownArrow.Assign(nil); // fixes GDI resource leak
|
|
DownArrow.LoadFromResourceName(HInstance, sSpinDownBtn);
|
|
DownArrow.TransparentColor := clWhite;
|
|
DownArrow.Transparent := True;
|
|
JvDrawArrows(ABitmap.Canvas, ADownState, Enabled, UpArrow, DownArrow);
|
|
end;
|
|
{$IFDEF VisualCLX}
|
|
Stop;
|
|
{$ENDIF VisualCLX}
|
|
end;
|
|
end;
|
|
|
|
procedure PoleDraw;
|
|
var
|
|
H: Integer;
|
|
TopFlags, BottomFlags: DWORD;
|
|
R, R1: TRect;
|
|
RSrc: TRect;
|
|
begin
|
|
TopFlags := EDGE_RAISED;
|
|
BottomFlags := EDGE_RAISED;
|
|
|
|
with ABitmap.Canvas do
|
|
begin
|
|
{$IFDEF VisualCLX}
|
|
Start;
|
|
{$ENDIF VisualCLX}
|
|
{ top glyph }
|
|
H := Height div 2;
|
|
R := Bounds(0, 0, Width, H);
|
|
if ADownState = sbTopDown then
|
|
TopFlags := EDGE_SUNKEN
|
|
else
|
|
R.Bottom := R.Bottom + 1;
|
|
if ADownState = sbBottomDown then
|
|
BottomFlags := EDGE_SUNKEN;
|
|
RSrc := R;
|
|
DrawEdge(Handle, R, TopFlags, BF_RECT or BF_SOFT or BF_ADJUST);
|
|
R1 := Bounds(0, H, Width, Height);
|
|
R1.Bottom := Height;
|
|
DrawEdge(Handle, R1, BottomFlags, BF_RECT or BF_SOFT or BF_ADJUST);
|
|
if not CustomGlyphs then
|
|
begin
|
|
UpArrow.Assign(nil); // fixes GDI resource leak
|
|
UpArrow.LoadFromResourceName(HInstance, sSpinUpBtnPole);
|
|
UpArrow.TransparentColor := clWhite;
|
|
UpArrow.Transparent := True;
|
|
DownArrow.Assign(nil); // fixes GDI resource leak
|
|
DownArrow.LoadFromResourceName(HInstance, sSpinDownBtnPole);
|
|
DownArrow.TransparentColor := clWhite;
|
|
DownArrow.Transparent := True;
|
|
PoleDrawArrows(ABitmap.Canvas, ADownState, Enabled, UpArrow, DownArrow);
|
|
end;
|
|
{$IFDEF VisualCLX}
|
|
Stop;
|
|
{$ENDIF VisualCLX}
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
UpArrow := nil;
|
|
DownArrow := nil;
|
|
try
|
|
if not CustomGlyphs then
|
|
begin
|
|
UpArrow := TBitmap.Create;
|
|
DownArrow := TBitmap.Create;
|
|
end;
|
|
|
|
ABitmap.Height := Height;
|
|
ABitmap.Width := Width;
|
|
|
|
with ABitmap.Canvas do
|
|
begin
|
|
ButtonRect := Bounds(0, 0, Width, Height);
|
|
Pen.Width := 1;
|
|
Brush.Color := clBtnFace;
|
|
Brush.Style := bsSolid;
|
|
FillRect(ButtonRect);
|
|
end;
|
|
if FStyle = sbsClassic then
|
|
PoleDraw
|
|
else
|
|
JvDraw;
|
|
finally
|
|
UpArrow.Free;
|
|
DownArrow.Free;
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF JVCLThemesEnabled}
|
|
|
|
procedure TSpinButtonBitmaps.DrawDiagonalThemedArrows(ACanvas: TCanvas; const AState: TSpinButtonState;
|
|
const Enabled: Boolean; AUpArrow, ADownArrow: TBitmap);
|
|
var
|
|
UpArrowPos, DownArrowPos: TPoint;
|
|
// UpArrowRect, DownArrowRect: TRect;
|
|
DisabledBitmap: TBitmap;
|
|
begin
|
|
{ Init arrow positions }
|
|
UpArrowPos := Point(
|
|
Round((Width / 4) - (AUpArrow.Width / 2)) + 1,
|
|
Round((Height / 4) - (AUpArrow.Height / 2)) + 1);
|
|
DownArrowPos := Point(
|
|
Round((3 * Width / 4) - (ADownArrow.Width / 2)) - 1,
|
|
Round((3 * Height / 4) - (ADownArrow.Height / 2)) - 1);
|
|
|
|
//UpArrowRect := Bounds(0, 0, AUpArrow.Width, AUpArrow.Height);
|
|
//DownArrowRect := Bounds(0, 0, ADownArrow.Width, ADownArrow.Height);
|
|
|
|
with ACanvas do
|
|
begin
|
|
{ Draw up arraw }
|
|
if Enabled then
|
|
begin
|
|
with UpArrowPos do
|
|
Draw(X, Y, AUpArrow)
|
|
end
|
|
else
|
|
begin
|
|
DisabledBitmap := CreateDisabledBitmap(AUpArrow, clBlack);
|
|
try
|
|
with UpArrowPos do
|
|
Draw(X, Y, DisabledBitmap);
|
|
finally
|
|
DisabledBitmap.Free;
|
|
end;
|
|
end;
|
|
|
|
{ Draw bottom arrow }
|
|
if Enabled then
|
|
with DownArrowPos do
|
|
Draw(X, Y, ADownArrow)
|
|
else
|
|
begin
|
|
DisabledBitmap := CreateDisabledBitmap(ADownArrow, clBlack);
|
|
try
|
|
with DownArrowPos do
|
|
Draw(X, Y, DisabledBitmap);
|
|
finally
|
|
DisabledBitmap.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{$ENDIF JVCLThemesEnabled}
|
|
|
|
procedure TSpinButtonBitmaps.DrawGlyphs(ACanvas: TCanvas; const AState: TSpinButtonState; const Enabled: Boolean;
|
|
AUpArrow, ADownArrow: TBitmap);
|
|
begin
|
|
{$IFDEF JVCLThemesEnabled}
|
|
if FIsThemed then
|
|
begin
|
|
if FStyle <> sbsClassic then
|
|
DrawDiagonalThemedArrows(ACanvas, AState, Enabled, AUpArrow, ADownArrow);
|
|
Exit;
|
|
end;
|
|
{$ENDIF JVCLThemesEnabled}
|
|
if FStyle = sbsClassic then
|
|
PoleDrawArrows(ACanvas, AState, Enabled, AUpArrow, ADownArrow)
|
|
else
|
|
JvDrawArrows(ACanvas, AState, Enabled, AUpArrow, ADownArrow)
|
|
end;
|
|
|
|
procedure TSpinButtonBitmaps.JvDrawArrows(ACanvas: TCanvas; const AState: TSpinButtonState;
|
|
const Enabled: Boolean; AUpArrow, ADownArrow: TBitmap);
|
|
var
|
|
Dest, Source: TRect;
|
|
DeltaRect: Integer;
|
|
DisabledBitmap: TBitmap;
|
|
begin
|
|
{ buttons }
|
|
with ACanvas do
|
|
begin
|
|
{ top glyph }
|
|
DeltaRect := 1;
|
|
if AState = sbTopDown then
|
|
Inc(DeltaRect);
|
|
|
|
Dest := Bounds(Round((Width / 4) - (AUpArrow.Width / 2)) + DeltaRect,
|
|
Round((Height / 4) - (AUpArrow.Height / 2)) + DeltaRect, AUpArrow.Width,
|
|
AUpArrow.Height);
|
|
Source := Bounds(0, 0, AUpArrow.Width, AUpArrow.Height);
|
|
|
|
if Enabled then
|
|
BrushCopy({$IFDEF VisualCLX} ACanvas, {$ENDIF} Dest, AUpArrow, Source, AUpArrow.TransparentColor)
|
|
else
|
|
begin
|
|
DisabledBitmap := CreateDisabledBitmap(AUpArrow, clBlack);
|
|
try
|
|
BrushCopy({$IFDEF VisualCLX} ACanvas, {$ENDIF} Dest, DisabledBitmap, Source, DisabledBitmap.TransparentColor);
|
|
finally
|
|
DisabledBitmap.Free;
|
|
end;
|
|
end;
|
|
|
|
{ bottom glyph }
|
|
Dest := Bounds(Round((3 * Width / 4) - (ADownArrow.Width / 2)) - 1,
|
|
Round((3 * Height / 4) - (ADownArrow.Height / 2)) - 1,
|
|
ADownArrow.Width, ADownArrow.Height);
|
|
Source := Bounds(0, 0, ADownArrow.Width, ADownArrow.Height);
|
|
|
|
if Enabled then
|
|
BrushCopy({$IFDEF VisualCLX} ACanvas, {$ENDIF} Dest, ADownArrow, Source, ADownArrow.TransparentColor)
|
|
else
|
|
begin
|
|
DisabledBitmap := CreateDisabledBitmap(ADownArrow, clBlack);
|
|
try
|
|
BrushCopy({$IFDEF VisualCLX} ACanvas, {$ENDIF} Dest, DisabledBitmap, Source, DisabledBitmap.TransparentColor);
|
|
finally
|
|
DisabledBitmap.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TSpinButtonBitmaps.PoleDrawArrows(ACanvas: TCanvas;
|
|
const AState: TSpinButtonState; const Enabled: Boolean; AUpArrow, ADownArrow: TBitmap);
|
|
var
|
|
X, Y, I, J, H: Integer;
|
|
R1: TRect;
|
|
R: TRect;
|
|
DisabledBitmap: TBitmap;
|
|
begin
|
|
with ACanvas do
|
|
begin
|
|
H := Height div 2;
|
|
R := Bounds(0, 0, Width, H);
|
|
if AState = sbTopDown then
|
|
else
|
|
R.Bottom := R.Bottom + 1;
|
|
R1 := Bounds(0, H, Width, Height);
|
|
R1.Bottom := Height;
|
|
I := R.Bottom - R.Top - 1;
|
|
J := R1.Bottom - R1.Top - 1;
|
|
Y := R.Top + (H - AUpArrow.Height) div 2;
|
|
if AState = sbTopDown then
|
|
OffsetRect(R1, 0, 1);
|
|
|
|
R1.Bottom := R1.Top + I;
|
|
if J - AUpArrow.Height < 0 then
|
|
Y := R.Top;
|
|
X := (Width - AUpArrow.Width) div 2;
|
|
|
|
IntersectClipRect(Handle, R.Left, R.Top, R.Right, R.Bottom);
|
|
if Enabled then
|
|
Draw(X, Y, AUpArrow)
|
|
else
|
|
begin
|
|
DisabledBitmap := CreateDisabledBitmap(AUpArrow, clBlack);
|
|
try
|
|
Draw(X, Y, DisabledBitmap);
|
|
finally
|
|
DisabledBitmap.Free;
|
|
end;
|
|
end;
|
|
SelectClipRgn(Handle, 0);
|
|
|
|
X := (Width - ADownArrow.Width) div 2;
|
|
Y := R1.Top + (I - ADownArrow.Height) div 2;
|
|
if I - ADownArrow.Height < 0 then
|
|
begin
|
|
Dec(R1.Top);
|
|
Y := R1.Bottom - ADownArrow.Height
|
|
end;
|
|
|
|
IntersectClipRect(Handle, R1.Left, R1.Top, R1.Right, R1.Bottom);
|
|
if Enabled then
|
|
Draw(X, Y, ADownArrow)
|
|
else
|
|
begin
|
|
DisabledBitmap := CreateDisabledBitmap(ADownArrow, clBlack);
|
|
try
|
|
Draw(X, Y, DisabledBitmap);
|
|
finally
|
|
DisabledBitmap.Free;
|
|
end;
|
|
end;
|
|
SelectClipRgn(Handle, 0);
|
|
end;
|
|
end;
|
|
|
|
procedure TSpinButtonBitmaps.RemoveClient;
|
|
begin
|
|
Dec(FClientCount);
|
|
if FClientCount = 0 then
|
|
Self.Free;
|
|
end;
|
|
|
|
procedure TSpinButtonBitmaps.Reset;
|
|
begin
|
|
FResetOnDraw := True;
|
|
end;
|
|
|
|
//=== { TSpinButtonBitmapsManager } ==========================================
|
|
|
|
constructor TSpinButtonBitmapsManager.Create;
|
|
begin
|
|
inherited Create;
|
|
FList := TList.Create;
|
|
end;
|
|
|
|
destructor TSpinButtonBitmapsManager.Destroy;
|
|
begin
|
|
while FList.Count > 0 do
|
|
// this will implicitly remove the object from the list
|
|
TObject(FList[0]).Free;
|
|
|
|
FList.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TSpinButtonBitmapsManager.AddClient;
|
|
begin
|
|
Inc(FClientCount);
|
|
end;
|
|
|
|
function TSpinButtonBitmapsManager.Find(const Width, Height: Integer;
|
|
const AButtonStyle: TJvSpinButtonStyle; const ACustomGlyphs: Boolean;
|
|
var Index: Integer): Boolean;
|
|
var
|
|
L, H, I, C: Integer;
|
|
begin
|
|
// same binary search as Classes.TStringList.Find
|
|
Result := False;
|
|
L := 0;
|
|
H := FList.Count - 1;
|
|
while L <= H do
|
|
begin
|
|
I := (L + H) shr 1;
|
|
C := TSpinButtonBitmaps(FList[I]).CompareWith(Width, Height, AButtonStyle, ACustomGlyphs);
|
|
if C < 0 then
|
|
L := I + 1
|
|
else
|
|
begin
|
|
H := I - 1;
|
|
if C = 0 then
|
|
begin
|
|
Result := True;
|
|
L := I;
|
|
end;
|
|
end;
|
|
end;
|
|
Index := L;
|
|
end;
|
|
|
|
procedure TSpinButtonBitmapsManager.Remove(Obj: TObject);
|
|
begin
|
|
FList.Remove(Obj);
|
|
end;
|
|
|
|
procedure TSpinButtonBitmapsManager.RemoveClient;
|
|
begin
|
|
Dec(FClientCount);
|
|
if FClientCount = 0 then
|
|
begin
|
|
if Self = GSpinButtonBitmapsManager then
|
|
GSpinButtonBitmapsManager := nil;
|
|
Self.Free;
|
|
end;
|
|
end;
|
|
|
|
function TSpinButtonBitmapsManager.WantButtons(const Width, Height: Integer;
|
|
const AButtonStyle: TJvSpinButtonStyle; const ACustomGlyphs: Boolean): TSpinButtonBitmaps;
|
|
var
|
|
Index: Integer;
|
|
begin
|
|
if not Find(Width, Height, AButtonStyle, ACustomGlyphs, Index) then
|
|
FList.Insert(Index, TSpinButtonBitmaps.Create(Self, Width, Height, AButtonStyle, ACustomGlyphs));
|
|
Result := TSpinButtonBitmaps(FList[Index]);
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
initialization
|
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
|
|
|
finalization
|
|
UnregisterUnitVersion(HInstance);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
end.
|