?? rxclock.pas
字號:
{*******************************************************}
{ }
{ Delphi VCL Extensions (RX) }
{ }
{ Copyright (c) 1995, 1996 AO ROSNO }
{ Copyright (c) 1997, 1998 Master-Bank }
{ }
{*******************************************************}
unit RXClock;
interface
{$I RX.INC}
uses Windows, SysUtils, Messages, Classes, Graphics, Controls,
Forms, StdCtrls, ExtCtrls, Menus, RxTimer, RTLConsts;
type
TShowClock = (scDigital, scAnalog);
TPaintMode = (pmPaintAll, pmHandPaint);
TRxClockTime = packed record
Hour, Minute, Second: Word;
end;
TRxGetTimeEvent = procedure (Sender: TObject; var ATime: TDateTime) of object;
{ TRxClock }
TRxClock = class(TCustomPanel)
private
{ Private declarations }
FTimer: TRxTimer;
FAutoSize: Boolean;
FShowMode: TShowClock;
FTwelveHour: Boolean;
FLeadingZero: Boolean;
FShowSeconds: Boolean;
FAlarm: TDateTime;
FAlarmEnabled: Boolean;
FHooked: Boolean;
FDotsColor: TColor;
FAlarmWait: Boolean;
FDisplayTime: TRxClockTime;
FClockRect: TRect;
FClockRadius: Longint;
FClockCenter: TPoint;
FOnGetTime: TRxGetTimeEvent;
FOnAlarm: TNotifyEvent;
procedure TimerExpired(Sender: TObject);
procedure GetTime(var T: TRxClockTime);
function IsAlarmTime(ATime: TDateTime): Boolean;
procedure SetShowMode(Value: TShowClock);
function GetAlarmElement(Index: Integer): Byte;
procedure SetAlarmElement(Index: Integer; Value: Byte);
procedure SetDotsColor(Value: TColor);
procedure SetTwelveHour(Value: Boolean);
procedure SetLeadingZero(Value: Boolean);
procedure SetShowSeconds(Value: Boolean);
procedure PaintAnalogClock(PaintMode: TPaintMode);
procedure Paint3DFrame(var Rect: TRect);
procedure DrawAnalogFace;
procedure CircleClock(MaxWidth, MaxHeight: Integer);
procedure DrawSecondHand(Pos: Integer);
procedure DrawFatHand(Pos: Integer; HourHand: Boolean);
procedure PaintTimeStr(var Rect: TRect; FullTime: Boolean);
procedure ResizeFont(const Rect: TRect);
procedure ResetAlarm;
procedure CheckAlarm;
function FormatSettingsChange(var Message: TMessage): Boolean;
procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED;
procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
procedure WMTimeChange(var Message: TMessage); message WM_TIMECHANGE;
protected
{ Protected declarations }
procedure SetAutoSize(Value: Boolean); override;
procedure Alarm; dynamic;
procedure AlignControls(AControl: TControl; var Rect: TRect); override;
procedure CreateWnd; override;
procedure DestroyWindowHandle; override;
procedure Loaded; override;
procedure Paint; override;
function GetSystemTime: TDateTime; virtual;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure SetAlarmTime(AlarmTime: TDateTime);
procedure UpdateClock;
published
{ Published declarations }
property AlarmEnabled: Boolean read FAlarmEnabled write FAlarmEnabled default False;
property AlarmHour: Byte Index 1 read GetAlarmElement write SetAlarmElement default 0;
property AlarmMinute: Byte Index 2 read GetAlarmElement write SetAlarmElement default 0;
property AlarmSecond: Byte Index 3 read GetAlarmElement write SetAlarmElement default 0;
property AutoSize: Boolean read FAutoSize write SetAutoSize default False;
property BevelInner default bvLowered;
property BevelOuter default bvRaised;
property DotsColor: TColor read FDotsColor write SetDotsColor default clTeal;
property ShowMode: TShowClock read FShowMode write SetShowMode default scDigital;
property ShowSeconds: Boolean read FShowSeconds write SetShowSeconds default True;
property TwelveHour: Boolean read FTwelveHour write SetTwelveHour default False;
property LeadingZero: Boolean read FLeadingZero write SetLeadingZero default True;
property Align;
property BevelWidth;
property BorderWidth;
property BorderStyle;
{$IFDEF RX_D4}
property Anchors;
property Constraints;
property UseDockManager default True;
property DockSite;
property DragKind;
property FullRepaint;
{$ENDIF}
property Color;
property Ctl3D;
property Cursor;
property DragMode;
property DragCursor;
property Enabled;
property Font;
property ParentColor;
property ParentCtl3D;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Visible;
property OnAlarm: TNotifyEvent read FOnAlarm write FOnAlarm;
property OnGetTime: TRxGetTimeEvent read FOnGetTime write FOnGetTime;
property OnClick;
property OnDblClick;
property OnMouseMove;
property OnMouseDown;
property OnMouseUp;
property OnDragOver;
property OnDragDrop;
property OnEndDrag;
property OnResize;
{$IFDEF RX_D5}
property OnContextPopup;
{$ENDIF}
{$IFDEF WIN32}
property OnStartDrag;
{$ENDIF}
{$IFDEF RX_D4}
property OnCanResize;
property OnConstrainedResize;
property OnDockDrop;
property OnDockOver;
property OnEndDock;
property OnGetSiteInfo;
property OnStartDock;
property OnUnDock;
{$ENDIF}
end;
implementation
uses Consts, VCLUtils;
const
Registered: Boolean = False;
type
PPointArray = ^TPointArray;
TPointArray = array [0..60 * 2 - 1] of TSmallPoint;
const
ClockData: array[0..60 * 4 - 1] of Byte = (
$00, $00, $C1, $E0, $44, $03, $EC, $E0, $7F, $06, $6F, $E1,
$A8, $09, $48, $E2, $B5, $0C, $74, $E3, $9F, $0F, $F0, $E4,
$5E, $12, $B8, $E6, $E9, $14, $C7, $E8, $39, $17, $17, $EB,
$48, $19, $A2, $ED, $10, $1B, $60, $F0, $8C, $1C, $4B, $F3,
$B8, $1D, $58, $F6, $91, $1E, $81, $F9, $14, $1F, $BC, $FC,
$40, $1F, $00, $00, $14, $1F, $44, $03, $91, $1E, $7F, $06,
$B8, $1D, $A8, $09, $8C, $1C, $B5, $0C, $10, $1B, $A0, $0F,
$48, $19, $5E, $12, $39, $17, $E9, $14, $E9, $14, $39, $17,
$5E, $12, $48, $19, $9F, $0F, $10, $1B, $B5, $0C, $8C, $1C,
$A8, $09, $B8, $1D, $7F, $06, $91, $1E, $44, $03, $14, $1F,
$00, $00, $3F, $1F, $BC, $FC, $14, $1F, $81, $F9, $91, $1E,
$58, $F6, $B8, $1D, $4B, $F3, $8C, $1C, $60, $F0, $10, $1B,
$A2, $ED, $48, $19, $17, $EB, $39, $17, $C7, $E8, $E9, $14,
$B8, $E6, $5E, $12, $F0, $E4, $9F, $0F, $74, $E3, $B5, $0C,
$48, $E2, $A8, $09, $6F, $E1, $7F, $06, $EC, $E0, $44, $03,
$C1, $E0, $00, $00, $EC, $E0, $BC, $FC, $6F, $E1, $81, $F9,
$48, $E2, $58, $F6, $74, $E3, $4B, $F3, $F0, $E4, $60, $F0,
$B8, $E6, $A2, $ED, $C7, $E8, $17, $EB, $17, $EB, $C7, $E8,
$A2, $ED, $B8, $E6, $61, $F0, $F0, $E4, $4B, $F3, $74, $E3,
$58, $F6, $48, $E2, $81, $F9, $6F, $E1, $BC, $FC, $EC, $E0);
const
AlarmSecDelay = 60; { seconds for try alarm event after alarm time occured }
MaxDotWidth = 25; { maximum Hour-marking dot width }
MinDotWidth = 2; { minimum Hour-marking dot width }
MinDotHeight = 1; { minimum Hour-marking dot height }
{ distance from the center of the clock to... }
HourSide = 7; { ...either side of the Hour hand }
MinuteSide = 5; { ...either side of the Minute hand }
HourTip = 60; { ...the tip of the Hour hand }
MinuteTip = 80; { ...the tip of the Minute hand }
SecondTip = 80; { ...the tip of the Second hand }
HourTail = 15; { ...the tail of the Hour hand }
MinuteTail = 20; { ...the tail of the Minute hand }
{ conversion factors }
CirTabScale = 8000; { circle table values scale down value }
MmPerDm = 100; { millimeters per decimeter }
{ number of hand positions on... }
HandPositions = 60; { ...entire clock }
SideShift = (HandPositions div 4); { ...90 degrees of clock }
TailShift = (HandPositions div 2); { ...180 degrees of clock }
var
CircleTab: PPointArray;
HRes: Integer; { width of the display (in pixels) }
VRes: Integer; { height of the display (in raster lines) }
AspectH: Longint; { number of pixels per decimeter on the display }
AspectV: Longint; { number of raster lines per decimeter on the display }
{ Exception routine }
procedure InvalidTime(Hour, Min, Sec: Word);
var
sTime: string[50];
begin
sTime := IntToStr(Hour) + TimeSeparator + IntToStr(Min) +
TimeSeparator + IntToStr(Sec);
raise EConvertError.CreateFmt(ResStr(SInvalidTime), [sTime]);
end;
function VertEquiv(l: Integer): Integer;
begin
VertEquiv := Longint(l) * AspectV div AspectH;
end;
function HorzEquiv(l: Integer): Integer;
begin
HorzEquiv := Longint(l) * AspectH div AspectV;
end;
function LightColor(Color: TColor): TColor;
var
L: Longint;
C: array[1..3] of Byte;
I: Byte;
begin
L := ColorToRGB(Color);
C[1] := GetRValue(L); C[2] := GetGValue(L); C[3] := GetBValue(L);
for I := 1 to 3 do begin
if C[I] = $FF then begin
Result := clBtnHighlight;
Exit;
end;
if C[I] <> 0 then
if C[I] = $C0 then C[I] := $FF
else C[I] := C[I] + $7F;
end;
Result := TColor(RGB(C[1], C[2], C[3]));
end;
procedure ClockInit;
var
Pos: Integer; { hand position Index into the circle table }
vSize: Integer; { height of the display in millimeters }
hSize: Integer; { width of the display in millimeters }
DC: HDC;
begin
DC := GetDC(0);
try
VRes := GetDeviceCaps(DC, VERTRES);
HRes := GetDeviceCaps(DC, HORZRES);
vSize := GetDeviceCaps(DC, VERTSIZE);
hSize := GetDeviceCaps(DC, HORZSIZE);
finally
ReleaseDC(0, DC);
end;
AspectV := (Longint(VRes) * MmPerDm) div Longint(vSize);
AspectH := (Longint(HRes) * MmPerDm) div Longint(hSize);
CircleTab := PPointArray(@ClockData);
for Pos := 0 to HandPositions - 1 do
CircleTab^[Pos].Y := VertEquiv(CircleTab^[Pos].Y);
end;
function HourHandPos(T: TRxClockTime): Integer;
begin
Result := (T.Hour * 5) + (T.Minute div 12);
end;
{ Digital clock font routine }
procedure SetNewFontSize(Canvas: TCanvas; const Text: string;
MaxH, MaxW: Integer);
const
fHeight = 1000;
var
Font: TFont;
NewH: Integer;
begin
Font := Canvas.Font;
{ empiric calculate character height by cell height }
MaxH := MulDiv(MaxH, 4, 5);
with Font do begin
Height := -fHeight;
NewH := MulDiv(fHeight, MaxW, Canvas.TextWidth(Text));
if NewH > MaxH then NewH := MaxH;
Height := -NewH;
end;
end;
{ TRxClock }
constructor TRxClock.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
if not Registered then begin
ClockInit;
Registered := True;
end;
Caption := TimeToStr(Time);
ControlStyle := ControlStyle - [csSetCaption]
{$IFDEF WIN32} - [csReplicatable] {$ENDIF};
BevelInner := bvLowered;
BevelOuter := bvRaised;
FTimer := TRxTimer.Create(Self);
FTimer.Interval := 450; { every second }
FTimer.OnTimer := TimerExpired;
FDotsColor := clTeal;
FShowSeconds := True;
FLeadingZero := True;
GetTime(FDisplayTime);
if FDisplayTime.Hour >= 12 then Dec(FDisplayTime.Hour, 12);
FAlarmWait := True;
FAlarm := EncodeTime(0, 0, 0, 0);
end;
destructor TRxClock.Destroy;
begin
if FHooked then begin
Application.UnhookMainWindow(FormatSettingsChange);
FHooked := False;
end;
inherited Destroy;
end;
procedure TRxClock.Loaded;
begin
inherited Loaded;
ResetAlarm;
end;
procedure TRxClock.CreateWnd;
begin
inherited CreateWnd;
if not (csDesigning in ComponentState) and not (IsLibrary or FHooked) then
begin
Application.HookMainWindow(FormatSettingsChange);
FHooked := True;
end;
end;
procedure TRxClock.DestroyWindowHandle;
begin
if FHooked then begin
Application.UnhookMainWindow(FormatSettingsChange);
FHooked := False;
end;
inherited DestroyWindowHandle;
end;
procedure TRxClock.CMCtl3DChanged(var Message: TMessage);
begin
inherited;
if ShowMode = scAnalog then Invalidate;
end;
procedure TRxClock.CMTextChanged(var Message: TMessage);
begin
{ Skip this message, no repaint }
end;
procedure TRxClock.CMFontChanged(var Message: TMessage);
begin
inherited;
Invalidate;
if AutoSize then Realign;
end;
procedure TRxClock.WMTimeChange(var Message: TMessage);
begin
inherited;
Invalidate;
CheckAlarm;
end;
function TRxClock.FormatSettingsChange(var Message: TMessage): Boolean;
begin
Result := False;
case Message.Msg of
WM_WININICHANGE:
begin
Invalidate;
if AutoSize then Realign;
end;
end;
end;
function TRxClock.GetSystemTime: TDateTime;
begin
Result := SysUtils.Time;
if Assigned(FOnGetTime) then FOnGetTime(Self, Result);
end;
procedure TRxClock.GetTime(var T: TRxClockTime);
var
MSec: Word;
begin
with T do
DecodeTime(GetSystemTime, Hour, Minute, Second, MSec);
end;
procedure TRxClock.UpdateClock;
begin
Invalidate;
if AutoSize then Realign;
Update;
end;
procedure TRxClock.ResetAlarm;
begin
FAlarmWait := (FAlarm > GetSystemTime) or (FAlarm = 0);
end;
function TRxClock.IsAlarmTime(ATime: TDateTime): Boolean;
var
Hour, Min, Sec, MSec: Word;
AHour, AMin, ASec: Word;
begin
DecodeTime(FAlarm, Hour, Min, Sec, MSec);
DecodeTime(ATime, AHour, AMin, ASec, MSec);
Result := {FAlarmWait and} (Hour = AHour) and (Min = AMin) and
(ASec >= Sec) and (ASec <= Sec + AlarmSecDelay);
end;
procedure TRxClock.ResizeFont(const Rect: TRect);
var
H, W: Integer;
DC: HDC;
TimeStr: string;
begin
H := Rect.Bottom - Rect.Top - 4;
W := (Rect.Right - Rect.Left - 30);
if (H <= 0) or (W <= 0) then Exit;
DC := GetDC(0);
try
Canvas.Handle := DC;
Canvas.Font := Font;
TimeStr := '88888';
if FShowSeconds then TimeStr := TimeStr + '888';
if FTwelveHour then begin
if Canvas.TextWidth(TimeAMString) > Canvas.TextWidth(TimePMString) then
TimeStr := TimeStr + ' ' + TimeAMString
else TimeStr := TimeStr + ' ' + TimePMString;
end;
SetNewFontSize(Canvas, TimeStr, H, W);
Font := Canvas.Font;
finally
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -