home *** CD-ROM | disk | FTP | other *** search
/ PC Format Collection 48 / SENT14D.ISO / tech / delphi / disk15 / sampsrc.pak / SPIN.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1995-08-24  |  15.4 KB  |  596 lines

  1. unit Spin;
  2.  
  3. interface
  4.  
  5. uses WinTypes, Classes, StdCtrls, ExtCtrls, Controls, Messages, SysUtils,
  6.   Forms, Graphics, Menus, Buttons;
  7.  
  8. const
  9.   InitRepeatPause = 400;  { pause before repeat timer (ms) }
  10.   RepeatPause     = 100;  { pause before hint window displays (ms)}
  11.  
  12. type
  13.  
  14.   TTimerSpeedButton = class;
  15.  
  16. { TSpinButton }
  17.  
  18.   TSpinButton = class (TWinControl)
  19.   private
  20.     FUpButton: TTimerSpeedButton;
  21.     FDownButton: TTimerSpeedButton;
  22.     FFocusedButton: TTimerSpeedButton;
  23.     FFocusControl: TWinControl;
  24.     FOnUpClick: TNotifyEvent;
  25.     FOnDownClick: TNotifyEvent;
  26.     function CreateButton: TTimerSpeedButton;
  27.     function GetUpGlyph: TBitmap;
  28.     function GetDownGlyph: TBitmap;
  29.     procedure SetUpGlyph(Value: TBitmap);
  30.     procedure SetDownGlyph(Value: TBitmap);
  31.     procedure BtnClick(Sender: TObject);
  32.     procedure BtnMouseDown (Sender: TObject; Button: TMouseButton;
  33.       Shift: TShiftState; X, Y: Integer);
  34.     procedure SetFocusBtn (Btn: TTimerSpeedButton);
  35.     procedure AdjustSize (var W: Integer; var H: Integer);
  36.     procedure WMSize(var Message: TWMSize);  message WM_SIZE;
  37.     procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
  38.     procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS;
  39.     procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  40.   protected
  41.     procedure Loaded; override;
  42.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  43.   public
  44.     constructor Create(AOwner: TComponent); override;
  45.     procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
  46.   published
  47.     property Align;
  48.     property Ctl3D;
  49.     property DownGlyph: TBitmap read GetDownGlyph write SetDownGlyph;
  50.     property DragCursor;
  51.     property DragMode;
  52.     property Enabled;
  53.     property FocusControl: TWinControl read FFocusControl write FFocusControl;
  54.     property ParentCtl3D;
  55.     property ParentShowHint;
  56.     property PopupMenu;
  57.     property ShowHint;
  58.     property TabOrder;
  59.     property TabStop;
  60.     property UpGlyph: TBitmap read GetUpGlyph write SetUpGlyph;
  61.     property Visible;
  62.     property OnDownClick: TNotifyEvent read FOnDownClick write FOnDownClick;
  63.     property OnDragDrop;
  64.     property OnDragOver;
  65.     property OnEndDrag;
  66.     property OnEnter;
  67.     property OnExit;
  68.     property OnUpClick: TNotifyEvent read FOnUpClick write FOnUpClick;
  69.   end;
  70.  
  71. { TSpinEdit }
  72.  
  73.   TSpinEdit = class(TCustomEdit)
  74.   private
  75.     FMinValue: LongInt;
  76.     FMaxValue: LongInt;
  77.     FCanvas: TCanvas;
  78.     FIncrement: LongInt;
  79.     FButton: TSpinButton;
  80.     FEditorEnabled: Boolean;
  81.     function GetMinHeight: Integer;
  82.     function GetValue: LongInt;
  83.     function CheckValue (NewValue: LongInt): LongInt;
  84.     procedure SetValue (NewValue: LongInt);
  85.     procedure SetEditRect;
  86.     procedure WMSize(var Message: TWMSize); message WM_SIZE;
  87.     procedure CMEnter(var Message: TCMGotFocus); message CM_ENTER;
  88.     procedure CMExit(var Message: TCMExit);   message CM_EXIT;
  89.     procedure WMPaste(var Message: TWMPaste);   message WM_PASTE;
  90.     procedure WMCut(var Message: TWMCut);   message WM_CUT;
  91.   protected
  92.     function IsValidChar(Key: Char): Boolean; virtual;
  93.     procedure UpClick (Sender: TObject); virtual;
  94.     procedure DownClick (Sender: TObject); virtual;
  95.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  96.     procedure KeyPress(var Key: Char); override;
  97.     procedure CreateParams(var Params: TCreateParams); override;
  98.     procedure CreateWnd; override;
  99.   public
  100.     constructor Create(AOwner: TComponent); override;
  101.     destructor Destroy; override;
  102.     property Button: TSpinButton read FButton;
  103.   published
  104.     property AutoSelect;
  105.     property AutoSize;
  106.     property Color;
  107.     property Ctl3D;
  108.     property DragCursor;
  109.     property DragMode;
  110.     property EditorEnabled: Boolean read FEditorEnabled write FEditorEnabled default True;
  111.     property Enabled;
  112.     property Font;
  113.     property Increment: LongInt read FIncrement write FIncrement default 1;
  114.     property MaxLength;
  115.     property MaxValue: LongInt read FMaxValue write FMaxValue;
  116.     property MinValue: LongInt read FMinValue write FMinValue;
  117.     property ParentColor;
  118.     property ParentCtl3D;
  119.     property ParentFont;
  120.     property ParentShowHint;
  121.     property PopupMenu;
  122.     property ReadOnly;
  123.     property ShowHint;
  124.     property TabOrder;
  125.     property TabStop;
  126.     property Value: LongInt read GetValue write SetValue;
  127.     property Visible;
  128.     property OnChange;
  129.     property OnClick;
  130.     property OnDblClick;
  131.     property OnDragDrop;
  132.     property OnDragOver;
  133.     property OnEndDrag;
  134.     property OnEnter;
  135.     property OnExit;
  136.     property OnKeyDown;
  137.     property OnKeyPress;
  138.     property OnKeyUp;
  139.     property OnMouseDown;
  140.     property OnMouseMove;
  141.     property OnMouseUp;
  142.   end;
  143.  
  144. { TTimerSpeedButton }
  145.  
  146.   TTimeBtnState = set of (tbFocusRect, tbAllowTimer);
  147.  
  148.   TTimerSpeedButton = class(TSpeedButton)
  149.   private
  150.     FRepeatTimer: TTimer;
  151.     FTimeBtnState: TTimeBtnState;
  152.     procedure TimerExpired(Sender: TObject);
  153.   protected
  154.     procedure Paint; override;
  155.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
  156.       X, Y: Integer); override;
  157.     procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
  158.       X, Y: Integer); override;
  159.   public
  160.     destructor Destroy; override;
  161.     property TimeBtnState: TTimeBtnState read FTimeBtnState write FTimeBtnState;
  162.   end;
  163.  
  164. implementation
  165.  
  166. uses WinProcs;
  167.  
  168. {$R SPIN}
  169.  
  170. { TSpinButton }
  171.  
  172. constructor TSpinButton.Create(AOwner: TComponent);
  173. begin
  174.   inherited Create(AOwner);
  175.   ControlStyle := ControlStyle - [csAcceptsControls, csSetCaption] +
  176.     [csFramed, csOpaque];
  177.  
  178.   FUpButton := CreateButton;
  179.   FDownButton := CreateButton;
  180.   UpGlyph := nil;
  181.   DownGlyph := nil;
  182.  
  183.   Width := 20;
  184.   Height := 25;
  185.   FFocusedButton := FUpButton;
  186. end;
  187.  
  188. function TSpinButton.CreateButton: TTimerSpeedButton;
  189. begin
  190.   Result := TTimerSpeedButton.Create (Self);
  191.   Result.OnClick := BtnClick;
  192.   Result.OnMouseDown := BtnMouseDown;
  193.   Result.Visible := True;
  194.   Result.Enabled := True;
  195.   Result.TimeBtnState := [tbAllowTimer];
  196.   Result.NumGlyphs := 1;
  197.   Result.Parent := Self;
  198. end;
  199.  
  200. procedure TSpinButton.AdjustSize (var W: Integer; var H: Integer);
  201. var
  202.   Y: Integer;
  203. begin
  204.   if (FUpButton = nil) or (csLoading in ComponentState) then Exit;
  205.   if W < 15 then W := 15;
  206.   FUpButton.SetBounds (0, 0, W, H div 2);
  207.   FDownButton.SetBounds (0, FUpButton.Height - 1, W, H - FUpButton.Height + 1);
  208. end;
  209.  
  210. procedure TSpinButton.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
  211. var
  212.   W, H: Integer;
  213. begin
  214.   W := AWidth;
  215.   H := AHeight;
  216.   AdjustSize (W, H);
  217.   inherited SetBounds (ALeft, ATop, W, H);
  218. end;
  219.  
  220. procedure TSpinButton.WMSize(var Message: TWMSize);
  221. var
  222.   W, H: Integer;
  223. begin
  224.   inherited;
  225.  
  226.   { check for minimum size }
  227.   W := Width;
  228.   H := Height;
  229.   AdjustSize (W, H);
  230.   if (W <> Width) or (H <> Height) then
  231.     inherited SetBounds(Left, Top, W, H);
  232.   Message.Result := 0;
  233. end;
  234.  
  235. procedure TSpinButton.WMSetFocus(var Message: TWMSetFocus);
  236. begin
  237.   FFocusedButton.TimeBtnState := FFocusedButton.TimeBtnState + [tbFocusRect];
  238.   FFocusedButton.Invalidate;
  239. end;
  240.  
  241. procedure TSpinButton.WMKillFocus(var Message: TWMKillFocus);
  242. begin
  243.   FFocusedButton.TimeBtnState := FFocusedButton.TimeBtnState - [tbFocusRect];
  244.   FFocusedButton.Invalidate;
  245. end;
  246.  
  247. procedure TSpinButton.KeyDown(var Key: Word; Shift: TShiftState);
  248. begin
  249.   case Key of
  250.     VK_UP:
  251.       begin
  252.         SetFocusBtn (FUpButton);
  253.         FUpButton.Click;
  254.       end;
  255.     VK_DOWN:
  256.       begin
  257.         SetFocusBtn (FDownButton);
  258.         FDownButton.Click;
  259.       end;
  260.     VK_SPACE:
  261.       FFocusedButton.Click;
  262.   end;
  263. end;
  264.  
  265. procedure TSpinButton.BtnMouseDown (Sender: TObject; Button: TMouseButton;
  266.   Shift: TShiftState; X, Y: Integer);
  267. begin
  268.   if Button = mbLeft then
  269.   begin
  270.     SetFocusBtn (TTimerSpeedButton (Sender));
  271.     if (FFocusControl <> nil) and FFocusControl.TabStop and 
  272.         FFocusControl.CanFocus and (GetFocus <> FFocusControl.Handle) then
  273.       FFocusControl.SetFocus
  274.     else if TabStop and (GetFocus <> Handle) and CanFocus then
  275.       SetFocus;
  276.   end;
  277. end;
  278.  
  279. procedure TSpinButton.BtnClick(Sender: TObject);
  280. begin
  281.   if Sender = FUpButton then
  282.   begin
  283.     if Assigned(FOnUpClick) then FOnUpClick(Self);
  284.   end
  285.   else
  286.     if Assigned(FOnDownClick) then FOnDownClick(Self);
  287. end;
  288.  
  289. procedure TSpinButton.SetFocusBtn (Btn: TTimerSpeedButton);
  290. begin
  291.   if TabStop and CanFocus and  (Btn <> FFocusedButton) then
  292.   begin
  293.     FFocusedButton.TimeBtnState := FFocusedButton.TimeBtnState - [tbFocusRect];
  294.     FFocusedButton := Btn;
  295.     if (GetFocus = Handle) then 
  296.     begin
  297.        FFocusedButton.TimeBtnState := FFocusedButton.TimeBtnState + [tbFocusRect];
  298.        Invalidate;
  299.     end;
  300.   end;   
  301. end;
  302.  
  303. procedure TSpinButton.WMGetDlgCode(var Message: TWMGetDlgCode);
  304. begin
  305.   Message.Result := DLGC_WANTARROWS;
  306. end;
  307.  
  308. procedure TSpinButton.Loaded;
  309. var
  310.   W, H: Integer;
  311. begin
  312.   inherited Loaded;
  313.   W := Width;
  314.   H := Height;
  315.   AdjustSize (W, H);
  316.   if (W <> Width) or (H <> Height) then
  317.     inherited SetBounds (Left, Top, W, H);
  318. end;
  319.  
  320. function TSpinButton.GetUpGlyph: TBitmap;
  321. begin
  322.   Result := FUpButton.Glyph;
  323. end;
  324.  
  325. procedure TSpinButton.SetUpGlyph(Value: TBitmap);
  326. begin
  327.   if Value <> nil then
  328.     FUpButton.Glyph := Value
  329.   else
  330.   begin
  331.     FUpButton.Glyph.Handle := LoadBitmap(HInstance, 'SpinUp');
  332.     FUpButton.NumGlyphs := 1;
  333.     FUpButton.Invalidate;
  334.   end;
  335. end;
  336.  
  337. function TSpinButton.GetDownGlyph: TBitmap;
  338. begin
  339.   Result := FDownButton.Glyph;
  340. end;
  341.  
  342. procedure TSpinButton.SetDownGlyph(Value: TBitmap);
  343. begin
  344.   if Value <> nil then
  345.     FDownButton.Glyph := Value
  346.   else
  347.   begin
  348.     FDownButton.Glyph.Handle := LoadBitmap(HInstance, 'SpinDown');
  349.     FDownButton.NumGlyphs := 1;
  350.     FDownButton.Invalidate;
  351.   end;
  352. end;
  353.  
  354. { TSpinEdit }
  355.  
  356. constructor TSpinEdit.Create(AOwner: TComponent);
  357. begin
  358.   inherited Create(AOwner);
  359.   FButton := TSpinButton.Create (Self);
  360.   FButton.Width := 15;
  361.   FButton.Height := 17;
  362.   FButton.Visible := True;  
  363.   FButton.Parent := Self;
  364.   FButton.FocusControl := Self;
  365.   FButton.OnUpClick := UpClick;
  366.   FButton.OnDownClick := DownClick;
  367.   Text := '0';
  368.   ControlStyle := ControlStyle - [csSetCaption];
  369.   FIncrement := 1;
  370.   FEditorEnabled := True;
  371. end;
  372.  
  373. destructor TSpinEdit.Destroy;
  374. begin
  375.   FButton := nil;
  376.   inherited Destroy;
  377. end;
  378.  
  379. procedure TSpinEdit.KeyDown(var Key: Word; Shift: TShiftState);
  380. begin
  381.   if Key = VK_UP then UpClick (Self)
  382.   else if Key = VK_DOWN then DownClick (Self);
  383.   inherited KeyDown(Key, Shift);
  384. end;
  385.  
  386. procedure TSpinEdit.KeyPress(var Key: Char);
  387. begin
  388.   if not IsValidChar(Key) then
  389.   begin
  390.     Key := #0;
  391.     MessageBeep(0)
  392.   end;
  393.   if Key <> #0 then inherited KeyPress(Key);
  394. end;
  395.  
  396. function TSpinEdit.IsValidChar(Key: Char): Boolean;
  397. begin
  398.   Result := (Key in [DecimalSeparator, '+', '-', '0'..'9']) or
  399.     ((Key < #32) and (Key <> Chr(VK_RETURN)));
  400.   if not FEditorEnabled and Result and ((Key >= #32) or
  401.       (Key = Char(VK_BACK)) or (Key = Char(VK_DELETE))) then
  402.     Result := False;
  403. end;
  404.  
  405. procedure TSpinEdit.CreateParams(var Params: TCreateParams);
  406. begin
  407.   inherited CreateParams(Params);
  408. {  Params.Style := Params.Style and not WS_BORDER;  }
  409.   Params.Style := Params.Style or ES_MULTILINE or WS_CLIPCHILDREN;
  410. end;
  411.  
  412. procedure TSpinEdit.CreateWnd;
  413. var
  414.   Loc: TRect;
  415. begin
  416.   inherited CreateWnd;
  417.   SetEditRect;
  418. end;
  419.  
  420. procedure TSpinEdit.SetEditRect;
  421. var
  422.   Loc: TRect;
  423. begin
  424.   SendMessage(Handle, EM_GETRECT, 0, LongInt(@Loc));
  425.   Loc.Bottom := ClientHeight + 1;  {+1 is workaround for windows paint bug}
  426.   Loc.Right := ClientWidth - FButton.Width - 2;
  427.   Loc.Top := 0;  
  428.   Loc.Left := 0;  
  429.   SendMessage(Handle, EM_SETRECTNP, 0, LongInt(@Loc));
  430.   SendMessage(Handle, EM_GETRECT, 0, LongInt(@Loc));  {debug}
  431. end;
  432.  
  433. procedure TSpinEdit.WMSize(var Message: TWMSize);
  434. var
  435.   Loc: TRect;
  436.   MinHeight: Integer;
  437. begin
  438.   inherited;
  439.   MinHeight := GetMinHeight;
  440.     { text edit bug: if size to less than minheight, then edit ctrl does
  441.       not display the text }
  442.   if Height < MinHeight then   
  443.     Height := MinHeight
  444.   else if FButton <> nil then
  445.   begin
  446.     FButton.SetBounds (Width - FButton.Width, 0, FButton.Width, Height);  
  447.     SetEditRect;
  448.   end;
  449. end;
  450.  
  451. function TSpinEdit.GetMinHeight: Integer;
  452. var
  453.   DC: HDC;
  454.   SaveFont: HFont;
  455.   I: Integer;
  456.   SysMetrics, Metrics: TTextMetric;
  457. begin
  458.   DC := GetDC(0);
  459.   GetTextMetrics(DC, SysMetrics);
  460.   SaveFont := SelectObject(DC, Font.Handle);
  461.   GetTextMetrics(DC, Metrics);
  462.   SelectObject(DC, SaveFont);
  463.   ReleaseDC(0, DC);
  464.   I := SysMetrics.tmHeight;
  465.   if I > Metrics.tmHeight then I := Metrics.tmHeight;
  466.   Result := Metrics.tmHeight + I div 4 + GetSystemMetrics(SM_CYBORDER) * 4 + 2;
  467. end;
  468.  
  469. procedure TSpinEdit.UpClick (Sender: TObject);
  470. begin
  471.   if ReadOnly then MessageBeep(0)
  472.   else Value := Value + FIncrement;
  473. end;
  474.  
  475. procedure TSpinEdit.DownClick (Sender: TObject);
  476. begin
  477.   if ReadOnly then MessageBeep(0)
  478.   else Value := Value - FIncrement;
  479. end;
  480.  
  481. procedure TSpinEdit.WMPaste(var Message: TWMPaste);   
  482. begin
  483.   if not FEditorEnabled or ReadOnly then Exit;
  484.   inherited;
  485. end;
  486.  
  487. procedure TSpinEdit.WMCut(var Message: TWMPaste);   
  488. begin
  489.   if not FEditorEnabled or ReadOnly then Exit;
  490.   inherited;
  491. end;
  492.  
  493. procedure TSpinEdit.CMExit(var Message: TCMExit);
  494. begin
  495.   inherited;
  496.   if CheckValue (Value) <> Value then
  497.     SetValue (Value);
  498. end;
  499.  
  500. function TSpinEdit.GetValue: LongInt;
  501. begin
  502.   try
  503.     Result := StrToInt (Text);
  504.   except
  505.     Result := FMinValue;
  506.   end;
  507. end;
  508.  
  509. procedure TSpinEdit.SetValue (NewValue: LongInt);
  510. begin
  511.   Text := IntToStr (CheckValue (NewValue));
  512. end;
  513.  
  514. function TSpinEdit.CheckValue (NewValue: LongInt): LongInt;
  515. begin
  516.   Result := NewValue;
  517.   if (FMaxValue <> FMinValue) then
  518.   begin
  519.     if NewValue < FMinValue then
  520.       Result := FMinValue
  521.     else if NewValue > FMaxValue then
  522.       Result := FMaxValue;
  523.   end;
  524. end;
  525.  
  526. procedure TSpinEdit.CMEnter(var Message: TCMGotFocus);
  527. begin
  528.   if AutoSelect and not (csLButtonDown in ControlState) then
  529.     SelectAll;
  530.   inherited;
  531. end;
  532.  
  533. {TTimerSpeedButton}
  534.  
  535. destructor TTimerSpeedButton.Destroy;
  536. begin
  537.   if FRepeatTimer <> nil then
  538.     FRepeatTimer.Free;
  539.   inherited Destroy;
  540. end;
  541.  
  542. procedure TTimerSpeedButton.MouseDown(Button: TMouseButton; Shift: TShiftState;
  543.   X, Y: Integer);
  544. begin
  545.   inherited MouseDown (Button, Shift, X, Y);
  546.   if tbAllowTimer in FTimeBtnState then
  547.   begin
  548.     if FRepeatTimer = nil then
  549.       FRepeatTimer := TTimer.Create(Self);
  550.  
  551.     FRepeatTimer.OnTimer := TimerExpired;
  552.     FRepeatTimer.Interval := InitRepeatPause;
  553.     FRepeatTimer.Enabled  := True;
  554.   end;
  555. end;
  556.  
  557. procedure TTimerSpeedButton.MouseUp(Button: TMouseButton; Shift: TShiftState;
  558.                                   X, Y: Integer);
  559. begin
  560.   inherited MouseUp (Button, Shift, X, Y);
  561.   if FRepeatTimer <> nil then
  562.     FRepeatTimer.Enabled  := False;
  563. end;
  564.  
  565. procedure TTimerSpeedButton.TimerExpired(Sender: TObject);
  566. begin
  567.   FRepeatTimer.Interval := RepeatPause;
  568.   if (FState = bsDown) and MouseCapture then
  569.   begin
  570.     try
  571.       Click;
  572.     except
  573.       FRepeatTimer.Enabled := False;
  574.       raise;
  575.     end;
  576.   end;
  577. end;
  578.  
  579. procedure TTimerSpeedButton.Paint;
  580. var
  581.   R: TRect;
  582. begin
  583.   inherited Paint;
  584.   if tbFocusRect in FTimeBtnState then
  585.   begin
  586.     R := Bounds(0, 0, Width, Height);
  587.     InflateRect(R, -3, -3);
  588.     if FState = bsDown then
  589.       OffsetRect(R, 1, 1);
  590.     DrawFocusRect(Canvas.Handle, R);
  591.   end;
  592. end;
  593.  
  594. end.
  595.  
  596.