Rxclock.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:28k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {*******************************************************}
  2. {                                                       }
  3. {         Delphi VCL Extensions (RX)                    }
  4. {                                                       }
  5. {         Copyright (c) 1995, 1996 AO ROSNO             }
  6. {         Copyright (c) 1997, 1998 Master-Bank          }
  7. {                                                       }
  8. {*******************************************************}
  9. unit RXClock;
  10. interface
  11. {$I RX.INC}
  12. uses Windows, SysUtils, Messages, Classes, Graphics, Controls,
  13.     Forms, StdCtrls, ExtCtrls, Menus, RxTimer, RTLConsts;
  14. type
  15.   TShowClock = (scDigital, scAnalog);
  16.   TPaintMode = (pmPaintAll, pmHandPaint);
  17.   TRxClockTime = packed record
  18.     Hour, Minute, Second: Word;
  19.   end;
  20.   TRxGetTimeEvent = procedure (Sender: TObject; var ATime: TDateTime) of object;
  21. { TRxClock }
  22.   TRxClock = class(TCustomPanel)
  23.   private
  24.     { Private declarations }
  25.     FTimer: TRxTimer;
  26.     FAutoSize: Boolean;
  27.     FShowMode: TShowClock;
  28.     FTwelveHour: Boolean;
  29.     FLeadingZero: Boolean;
  30.     FShowSeconds: Boolean;
  31.     FAlarm: TDateTime;
  32.     FAlarmEnabled: Boolean;
  33.     FHooked: Boolean;
  34.     FDotsColor: TColor;
  35.     FAlarmWait: Boolean;
  36.     FDisplayTime: TRxClockTime;
  37.     FClockRect: TRect;
  38.     FClockRadius: Longint;
  39.     FClockCenter: TPoint;
  40.     FOnGetTime: TRxGetTimeEvent;
  41.     FOnAlarm: TNotifyEvent;
  42.     procedure TimerExpired(Sender: TObject);
  43.     procedure GetTime(var T: TRxClockTime);
  44.     function IsAlarmTime(ATime: TDateTime): Boolean;
  45.     procedure SetShowMode(Value: TShowClock);
  46.     function GetAlarmElement(Index: Integer): Byte;
  47.     procedure SetAlarmElement(Index: Integer; Value: Byte);
  48.     procedure SetAutoSize(Value: Boolean);
  49.     procedure SetDotsColor(Value: TColor);
  50.     procedure SetTwelveHour(Value: Boolean);
  51.     procedure SetLeadingZero(Value: Boolean);
  52.     procedure SetShowSeconds(Value: Boolean);
  53.     procedure PaintAnalogClock(PaintMode: TPaintMode);
  54.     procedure Paint3DFrame(var Rect: TRect);
  55.     procedure DrawAnalogFace;
  56.     procedure CircleClock(MaxWidth, MaxHeight: Integer);
  57.     procedure DrawSecondHand(Pos: Integer);
  58.     procedure DrawFatHand(Pos: Integer; HourHand: Boolean);
  59.     procedure PaintTimeStr(var Rect: TRect; FullTime: Boolean);
  60.     procedure ResizeFont(const Rect: TRect);
  61.     procedure ResetAlarm;
  62.     procedure CheckAlarm;
  63.     function FormatSettingsChange(var Message: TMessage): Boolean;
  64.     procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED;
  65.     procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
  66.     procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  67.     procedure WMTimeChange(var Message: TMessage); message WM_TIMECHANGE;
  68.   protected
  69.     { Protected declarations }
  70.     procedure Alarm; dynamic;
  71.     procedure AlignControls(AControl: TControl; var Rect: TRect); override;
  72.     procedure CreateWnd; override;
  73.     procedure DestroyWindowHandle; override;
  74.     procedure Loaded; override;
  75.     procedure Paint; override;
  76.     function GetSystemTime: TDateTime; virtual;
  77.   public
  78.     { Public declarations }
  79.     constructor Create(AOwner: TComponent); override;
  80.     destructor Destroy; override;
  81.     procedure SetAlarmTime(AlarmTime: TDateTime);
  82.     procedure UpdateClock;
  83.   published
  84.     { Published declarations }
  85.     property AlarmEnabled: Boolean read FAlarmEnabled write FAlarmEnabled default False;
  86.     property AlarmHour: Byte Index 1 read GetAlarmElement write SetAlarmElement default 0;
  87.     property AlarmMinute: Byte Index 2 read GetAlarmElement write SetAlarmElement default 0;
  88.     property AlarmSecond: Byte Index 3 read GetAlarmElement write SetAlarmElement default 0;
  89.     property AutoSize: Boolean read FAutoSize write SetAutoSize default False;
  90.     property BevelInner default bvLowered;
  91.     property BevelOuter default bvRaised;
  92.     property DotsColor: TColor read FDotsColor write SetDotsColor default clTeal;
  93.     property ShowMode: TShowClock read FShowMode write SetShowMode default scDigital;
  94.     property ShowSeconds: Boolean read FShowSeconds write SetShowSeconds default True;
  95.     property TwelveHour: Boolean read FTwelveHour write SetTwelveHour default False;
  96.     property LeadingZero: Boolean read FLeadingZero write SetLeadingZero default True;
  97.     property Align;
  98.     property BevelWidth;
  99.     property BorderWidth;
  100.     property BorderStyle;
  101. {$IFDEF RX_D4}
  102.     property Anchors;
  103.     property Constraints;
  104.     property UseDockManager default True;
  105.     property DockSite;
  106.     property DragKind;
  107.     property FullRepaint;
  108. {$ENDIF}
  109.     property Color;
  110.     property Ctl3D;
  111.     property Cursor;
  112.     property DragMode;
  113.     property DragCursor;
  114.     property Enabled;
  115.     property Font;
  116.     property ParentColor;
  117.     property ParentCtl3D;
  118.     property ParentFont;
  119.     property ParentShowHint;
  120.     property PopupMenu;
  121.     property ShowHint;
  122.     property Visible;
  123.     property OnAlarm: TNotifyEvent read FOnAlarm write FOnAlarm;
  124.     property OnGetTime: TRxGetTimeEvent read FOnGetTime write FOnGetTime;
  125.     property OnClick;
  126.     property OnDblClick;
  127.     property OnMouseMove;
  128.     property OnMouseDown;
  129.     property OnMouseUp;
  130.     property OnDragOver;
  131.     property OnDragDrop;
  132.     property OnEndDrag;
  133.     property OnResize;
  134. {$IFDEF RX_D5}
  135.     property OnContextPopup;
  136. {$ENDIF}
  137. {$IFDEF WIN32}
  138.     property OnStartDrag;
  139. {$ENDIF}
  140. {$IFDEF RX_D4}
  141.     property OnCanResize;
  142.     property OnConstrainedResize;
  143.     property OnDockDrop;
  144.     property OnDockOver;
  145.     property OnEndDock;
  146.     property OnGetSiteInfo;
  147.     property OnStartDock;
  148.     property OnUnDock;
  149. {$ENDIF}
  150.   end;
  151. implementation
  152. uses Consts, VCLUtils;
  153. const
  154.   Registered: Boolean = False;
  155. type
  156.   PPointArray = ^TPointArray;
  157.   TPointArray = array [0..60 * 2 - 1] of TSmallPoint;
  158. const
  159.   ClockData: array[0..60 * 4 - 1] of Byte = (
  160.     $00, $00, $C1, $E0, $44, $03, $EC, $E0, $7F, $06, $6F, $E1,
  161.     $A8, $09, $48, $E2, $B5, $0C, $74, $E3, $9F, $0F, $F0, $E4,
  162.     $5E, $12, $B8, $E6, $E9, $14, $C7, $E8, $39, $17, $17, $EB,
  163.     $48, $19, $A2, $ED, $10, $1B, $60, $F0, $8C, $1C, $4B, $F3,
  164.     $B8, $1D, $58, $F6, $91, $1E, $81, $F9, $14, $1F, $BC, $FC,
  165.     $40, $1F, $00, $00, $14, $1F, $44, $03, $91, $1E, $7F, $06,
  166.     $B8, $1D, $A8, $09, $8C, $1C, $B5, $0C, $10, $1B, $A0, $0F,
  167.     $48, $19, $5E, $12, $39, $17, $E9, $14, $E9, $14, $39, $17,
  168.     $5E, $12, $48, $19, $9F, $0F, $10, $1B, $B5, $0C, $8C, $1C,
  169.     $A8, $09, $B8, $1D, $7F, $06, $91, $1E, $44, $03, $14, $1F,
  170.     $00, $00, $3F, $1F, $BC, $FC, $14, $1F, $81, $F9, $91, $1E,
  171.     $58, $F6, $B8, $1D, $4B, $F3, $8C, $1C, $60, $F0, $10, $1B,
  172.     $A2, $ED, $48, $19, $17, $EB, $39, $17, $C7, $E8, $E9, $14,
  173.     $B8, $E6, $5E, $12, $F0, $E4, $9F, $0F, $74, $E3, $B5, $0C,
  174.     $48, $E2, $A8, $09, $6F, $E1, $7F, $06, $EC, $E0, $44, $03,
  175.     $C1, $E0, $00, $00, $EC, $E0, $BC, $FC, $6F, $E1, $81, $F9,
  176.     $48, $E2, $58, $F6, $74, $E3, $4B, $F3, $F0, $E4, $60, $F0,
  177.     $B8, $E6, $A2, $ED, $C7, $E8, $17, $EB, $17, $EB, $C7, $E8,
  178.     $A2, $ED, $B8, $E6, $61, $F0, $F0, $E4, $4B, $F3, $74, $E3,
  179.     $58, $F6, $48, $E2, $81, $F9, $6F, $E1, $BC, $FC, $EC, $E0);
  180. const
  181.   AlarmSecDelay = 60; { seconds for try alarm event after alarm time occured }
  182.   MaxDotWidth   = 25; { maximum Hour-marking dot width  }
  183.   MinDotWidth   = 2;  { minimum Hour-marking dot width  }
  184.   MinDotHeight  = 1;  { minimum Hour-marking dot height }
  185.   { distance from the center of the clock to... }
  186.   HourSide   = 7;   { ...either side of the Hour hand   }
  187.   MinuteSide = 5;   { ...either side of the Minute hand }
  188.   HourTip    = 60;  { ...the tip of the Hour hand       }
  189.   MinuteTip  = 80;  { ...the tip of the Minute hand     }
  190.   SecondTip  = 80;  { ...the tip of the Second hand     }
  191.   HourTail   = 15;  { ...the tail of the Hour hand      }
  192.   MinuteTail = 20;  { ...the tail of the Minute hand    }
  193.   { conversion factors }
  194.   CirTabScale = 8000; { circle table values scale down value  }
  195.   MmPerDm     = 100;  { millimeters per decimeter             }
  196.   { number of hand positions on... }
  197.   HandPositions = 60;                    { ...entire clock         }
  198.   SideShift     = (HandPositions div 4); { ...90 degrees of clock  }
  199.   TailShift     = (HandPositions div 2); { ...180 degrees of clock }
  200. var
  201.   CircleTab: PPointArray;
  202.   HRes: Integer;    { width of the display (in pixels)                    }
  203.   VRes: Integer;    { height of the display (in raster lines)             }
  204.   AspectH: Longint; { number of pixels per decimeter on the display       }
  205.   AspectV: Longint; { number of raster lines per decimeter on the display }
  206. { Exception routine }
  207. procedure InvalidTime(Hour, Min, Sec: Word);
  208. var
  209.   sTime: string[50];
  210. begin
  211.   sTime := IntToStr(Hour) + TimeSeparator + IntToStr(Min) +
  212.     TimeSeparator + IntToStr(Sec);
  213.   raise EConvertError.CreateFmt(ResStr(SInvalidTime), [sTime]);
  214. end;
  215. function VertEquiv(l: Integer): Integer;
  216. begin
  217.   VertEquiv := Longint(l) * AspectV div AspectH;
  218. end;
  219. function HorzEquiv(l: Integer): Integer;
  220. begin
  221.   HorzEquiv := Longint(l) * AspectH div AspectV;
  222. end;
  223. function LightColor(Color: TColor): TColor;
  224. var
  225.   L: Longint;
  226.   C: array[1..3] of Byte;
  227.   I: Byte;
  228. begin
  229.   L := ColorToRGB(Color);
  230.   C[1] := GetRValue(L); C[2] := GetGValue(L); C[3] := GetBValue(L);
  231.   for I := 1 to 3 do begin
  232.     if C[I] = $FF then begin
  233.       Result := clBtnHighlight;
  234.       Exit;
  235.     end;
  236.     if C[I] <> 0 then
  237.       if C[I] = $C0 then C[I] := $FF
  238.       else C[I] := C[I] + $7F;
  239.   end;
  240.   Result := TColor(RGB(C[1], C[2], C[3]));
  241. end;
  242. procedure ClockInit;
  243. var
  244.   Pos: Integer;   { hand position Index into the circle table }
  245.   vSize: Integer; { height of the display in millimeters      }
  246.   hSize: Integer; { width of the display in millimeters       }
  247.   DC: HDC;
  248. begin
  249.   DC := GetDC(0);
  250.   try
  251.     VRes := GetDeviceCaps(DC, VERTRES);
  252.     HRes := GetDeviceCaps(DC, HORZRES);
  253.     vSize := GetDeviceCaps(DC, VERTSIZE);
  254.     hSize := GetDeviceCaps(DC, HORZSIZE);
  255.   finally
  256.     ReleaseDC(0, DC);
  257.   end;
  258.   AspectV := (Longint(VRes) * MmPerDm) div Longint(vSize);
  259.   AspectH := (Longint(HRes) * MmPerDm) div Longint(hSize);
  260.   CircleTab := PPointArray(@ClockData);
  261.   for Pos := 0 to HandPositions - 1 do
  262.     CircleTab^[Pos].Y := VertEquiv(CircleTab^[Pos].Y);
  263. end;
  264. function HourHandPos(T: TRxClockTime): Integer;
  265. begin
  266.   Result := (T.Hour * 5) + (T.Minute div 12);
  267. end;
  268. { Digital clock font routine }
  269. procedure SetNewFontSize(Canvas: TCanvas; const Text: string;
  270.   MaxH, MaxW: Integer);
  271. const
  272.   fHeight = 1000;
  273. var
  274.   Font: TFont;
  275.   NewH: Integer;
  276. begin
  277.   Font := Canvas.Font;
  278.   { empiric calculate character height by cell height }
  279.   MaxH := MulDiv(MaxH, 4, 5);
  280.   with Font do begin
  281.     Height := -fHeight;
  282.     NewH := MulDiv(fHeight, MaxW, Canvas.TextWidth(Text));
  283.     if NewH > MaxH then NewH := MaxH;
  284.     Height := -NewH;
  285.   end;
  286. end;
  287. { TRxClock }
  288. constructor TRxClock.Create(AOwner: TComponent);
  289. begin
  290.   inherited Create(AOwner);
  291.   if not Registered then begin
  292.     ClockInit;
  293.     Registered := True;
  294.   end;
  295.   Caption := TimeToStr(Time);
  296.   ControlStyle := ControlStyle - [csSetCaption] 
  297.     {$IFDEF WIN32} - [csReplicatable] {$ENDIF};
  298.   BevelInner := bvLowered;
  299.   BevelOuter := bvRaised;
  300.   FTimer := TRxTimer.Create(Self);
  301.   FTimer.Interval := 450; { every second }
  302.   FTimer.OnTimer := TimerExpired;
  303.   FDotsColor := clTeal;
  304.   FShowSeconds := True;
  305.   FLeadingZero := True;
  306.   GetTime(FDisplayTime);
  307.   if FDisplayTime.Hour >= 12 then Dec(FDisplayTime.Hour, 12);
  308.   FAlarmWait := True;
  309.   FAlarm := EncodeTime(0, 0, 0, 0);
  310. end;
  311. destructor TRxClock.Destroy;
  312. begin
  313.   if FHooked then begin
  314.     Application.UnhookMainWindow(FormatSettingsChange);
  315.     FHooked := False;
  316.   end;
  317.   inherited Destroy;
  318. end;
  319. procedure TRxClock.Loaded;
  320. begin
  321.   inherited Loaded;
  322.   ResetAlarm;
  323. end;
  324. procedure TRxClock.CreateWnd;
  325. begin
  326.   inherited CreateWnd;
  327.   if not (csDesigning in ComponentState) and not (IsLibrary or FHooked) then
  328.   begin
  329.     Application.HookMainWindow(FormatSettingsChange);
  330.     FHooked := True;
  331.   end;
  332. end;
  333. procedure TRxClock.DestroyWindowHandle;
  334. begin
  335.   if FHooked then begin
  336.     Application.UnhookMainWindow(FormatSettingsChange);
  337.     FHooked := False;
  338.   end;
  339.   inherited DestroyWindowHandle;
  340. end;
  341. procedure TRxClock.CMCtl3DChanged(var Message: TMessage);
  342. begin
  343.   inherited;
  344.   if ShowMode = scAnalog then Invalidate;
  345. end;
  346. procedure TRxClock.CMTextChanged(var Message: TMessage);
  347. begin
  348.   { Skip this message, no repaint }
  349. end;
  350. procedure TRxClock.CMFontChanged(var Message: TMessage);
  351. begin
  352.   inherited;
  353.   Invalidate;
  354.   if AutoSize then Realign;
  355. end;
  356. procedure TRxClock.WMTimeChange(var Message: TMessage);
  357. begin
  358.   inherited;
  359.   Invalidate;
  360.   CheckAlarm;
  361. end;
  362. function TRxClock.FormatSettingsChange(var Message: TMessage): Boolean;
  363. begin
  364.   Result := False;
  365.   case Message.Msg of
  366.     WM_WININICHANGE:
  367.       begin
  368.         Invalidate;
  369.         if AutoSize then Realign;
  370.       end;
  371.   end;
  372. end;
  373. function TRxClock.GetSystemTime: TDateTime;
  374. begin
  375.   Result := SysUtils.Time;
  376.   if Assigned(FOnGetTime) then FOnGetTime(Self, Result);
  377. end;
  378. procedure TRxClock.GetTime(var T: TRxClockTime);
  379. var
  380.   MSec: Word;
  381. begin
  382.   with T do
  383.     DecodeTime(GetSystemTime, Hour, Minute, Second, MSec);
  384. end;
  385. procedure TRxClock.UpdateClock;
  386. begin
  387.   Invalidate;
  388.   if AutoSize then Realign;
  389.   Update;
  390. end;
  391. procedure TRxClock.ResetAlarm;
  392. begin
  393.   FAlarmWait := (FAlarm > GetSystemTime) or (FAlarm = 0);
  394. end;
  395. function TRxClock.IsAlarmTime(ATime: TDateTime): Boolean;
  396. var
  397.   Hour, Min, Sec, MSec: Word;
  398.   AHour, AMin, ASec: Word;
  399. begin
  400.   DecodeTime(FAlarm, Hour, Min, Sec, MSec);
  401.   DecodeTime(ATime, AHour, AMin, ASec, MSec);
  402.   Result := {FAlarmWait and} (Hour = AHour) and (Min = AMin) and
  403.     (ASec >= Sec) and (ASec <= Sec + AlarmSecDelay);
  404. end;
  405. procedure TRxClock.ResizeFont(const Rect: TRect);
  406. var
  407.   H, W: Integer;
  408.   DC: HDC;
  409.   TimeStr: string;
  410. begin
  411.   H := Rect.Bottom - Rect.Top - 4;
  412.   W := (Rect.Right - Rect.Left - 30);
  413.   if (H <= 0) or (W <= 0) then Exit;
  414.   DC := GetDC(0);
  415.   try
  416.     Canvas.Handle := DC;
  417.     Canvas.Font := Font;
  418.     TimeStr := '88888';
  419.     if FShowSeconds then TimeStr := TimeStr + '888';
  420.     if FTwelveHour then begin
  421.       if Canvas.TextWidth(TimeAMString) > Canvas.TextWidth(TimePMString) then
  422.         TimeStr := TimeStr + ' ' + TimeAMString
  423.       else TimeStr := TimeStr + ' ' + TimePMString;
  424.     end;
  425.     SetNewFontSize(Canvas, TimeStr, H, W);
  426.     Font := Canvas.Font;
  427.   finally
  428.     Canvas.Handle := 0;
  429.     ReleaseDC(0, DC);
  430.   end;
  431. end;
  432. procedure TRxClock.AlignControls(AControl: TControl; var Rect: TRect);
  433. {$IFDEF RX_D4}
  434. var
  435.   InflateWidth: Integer;
  436. {$ENDIF}
  437. begin
  438.   inherited AlignControls(AControl, Rect);
  439.   FClockRect := Rect;
  440. {$IFDEF RX_D4}
  441.   InflateWidth := BorderWidth + 1;
  442.   if BevelOuter <> bvNone then Inc(InflateWidth, BevelWidth);
  443.   if BevelInner <> bvNone then Inc(InflateWidth, BevelWidth);
  444.   InflateRect(FClockRect, -InflateWidth, -InflateWidth);
  445. {$ENDIF}
  446.   with FClockRect do CircleClock(Right - Left, Bottom - Top);
  447.   if AutoSize then ResizeFont(Rect);
  448. end;
  449. procedure TRxClock.Alarm;
  450. begin
  451.   if Assigned(FOnAlarm) then FOnAlarm(Self);
  452. end;
  453. procedure TRxClock.SetAutoSize(Value: Boolean);
  454. begin
  455.   if (Value <> FAutoSize) then begin
  456.     FAutoSize := Value;
  457.     if FAutoSize then begin
  458.       Invalidate;
  459.       Realign;
  460.     end;
  461.   end;
  462. end;
  463. procedure TRxClock.SetTwelveHour(Value: Boolean);
  464. begin
  465.   if FTwelveHour <> Value then begin
  466.     FTwelveHour := Value;
  467.     Invalidate;
  468.     if AutoSize then Realign;
  469.   end;
  470. end;
  471. procedure TRxClock.SetLeadingZero(Value: Boolean);
  472. begin
  473.   if FLeadingZero <> Value then begin
  474.     FLeadingZero := Value;
  475.     Invalidate;
  476.   end;
  477. end;
  478. procedure TRxClock.SetShowSeconds(Value: Boolean);
  479. begin
  480.   if FShowSeconds <> Value then begin
  481.     {if FShowSeconds and (ShowMode = scAnalog) then
  482.       DrawSecondHand(FDisplayTime.Second);}
  483.     FShowSeconds := Value;
  484.     Invalidate;
  485.     if AutoSize then Realign;
  486.   end;
  487. end;
  488. procedure TRxClock.SetDotsColor(Value: TColor);
  489. begin
  490.   if Value <> FDotsColor then begin
  491.     FDotsColor := Value;
  492.     Invalidate;
  493.   end;
  494. end;
  495. procedure TRxClock.SetShowMode(Value: TShowClock);
  496. begin
  497.   if FShowMode <> Value then begin
  498.     FShowMode := Value;
  499.     Invalidate;
  500.   end;
  501. end;
  502. function TRxClock.GetAlarmElement(Index: Integer): Byte;
  503. var
  504.   Hour, Min, Sec, MSec: Word;
  505. begin
  506.   DecodeTime(FAlarm, Hour, Min, Sec, MSec);
  507.   case Index of
  508.     1: Result := Hour;
  509.     2: Result := Min;
  510.     3: Result := Sec;
  511.     else Result := 0;
  512.   end;
  513. end;
  514. procedure TRxClock.SetAlarmElement(Index: Integer; Value: Byte);
  515. var
  516.   Hour, Min, Sec, MSec: Word;
  517. begin
  518.   DecodeTime(FAlarm, Hour, Min, Sec, MSec);
  519.   case Index of
  520.     1: Hour := Value;
  521.     2: Min := Value;
  522.     3: Sec := Value;
  523.     else Exit;
  524.   end;
  525.   if (Hour < 24) and (Min < 60) and (Sec < 60) then begin
  526.     FAlarm := EncodeTime(Hour, Min, Sec, 0);
  527.     ResetAlarm;
  528.   end
  529.   else InvalidTime(Hour, Min, Sec);
  530. end;
  531. procedure TRxClock.SetAlarmTime(AlarmTime: TDateTime);
  532. var
  533.   Hour, Min, Sec, MSec: Word;
  534. begin
  535.   DecodeTime(FAlarm, Hour, Min, Sec, MSec);
  536.   if (Hour < 24) and (Min < 60) and (Sec < 60) then begin
  537.     FAlarm := Frac(AlarmTime);
  538.     ResetAlarm;
  539.   end
  540.   else InvalidTime(Hour, Min, Sec);
  541. end;
  542. procedure TRxClock.TimerExpired(Sender: TObject);
  543. var
  544.   DC: HDC;
  545.   Rect: TRect;
  546.   InflateWidth: Integer;
  547. begin
  548.   DC := GetDC(Handle);
  549.   try
  550.     Canvas.Handle := DC;
  551.     Canvas.Brush.Color := Color;
  552.     Canvas.Font := Font;
  553.     Canvas.Pen.Color := Font.Color;
  554.     if FShowMode = scAnalog then PaintAnalogClock(pmHandPaint)
  555.     else begin
  556.       Rect := GetClientRect;
  557.       InflateWidth := BorderWidth;
  558.       if BevelOuter <> bvNone then Inc(InflateWidth, BevelWidth);
  559.       if BevelInner <> bvNone then Inc(InflateWidth, BevelWidth);
  560.       InflateRect(Rect, -InflateWidth, -InflateWidth);
  561.       PaintTimeStr(Rect, False);
  562.     end;
  563.   finally
  564.     Canvas.Handle := 0;
  565.     ReleaseDC(Handle, DC);
  566.   end;
  567.   CheckAlarm;
  568. end;
  569. procedure TRxClock.CheckAlarm;
  570. begin
  571.   if FAlarmEnabled and IsAlarmTime(GetSystemTime) then begin
  572.     if FAlarmWait then begin
  573.       FAlarmWait := False;
  574.       Alarm;
  575.     end;
  576.   end
  577.   else ResetAlarm;
  578. end;
  579. procedure TRxClock.DrawAnalogFace;
  580. var
  581.   Pos, DotHeight, DotWidth: Integer;
  582.   DotCenter: TPoint;
  583.   R: TRect;
  584.   SaveBrush, SavePen: TColor;
  585.   MinDots: Boolean;
  586. begin
  587.   DotWidth := (MaxDotWidth * Longint(FClockRect.Right - FClockRect.Left)) div HRes;
  588.   DotHeight := VertEquiv(DotWidth);
  589.   if DotHeight < MinDotHeight then DotHeight := MinDotHeight;
  590.   if DotWidth < MinDotWidth then DotWidth := MinDotWidth;
  591.   DotCenter.X := DotWidth div 2;
  592.   DotCenter.Y := DotHeight div 2;
  593.   InflateRect(FClockRect, -DotCenter.Y, -DotCenter.X);
  594.   FClockRadius := ((FClockRect.Right - FClockRect.Left) div 2);
  595.   FClockCenter.X := FClockRect.Left + FClockRadius;
  596.   FClockCenter.Y := FClockRect.Top + ((FClockRect.Bottom - FClockRect.Top) div 2);
  597.   InflateRect(FClockRect, DotCenter.Y, DotCenter.X);
  598.   SaveBrush := Canvas.Brush.Color;
  599.   SavePen := Canvas.Pen.Color;
  600.   try
  601.     Canvas.Brush.Color := Canvas.Pen.Color;
  602.     MinDots := ((DotWidth > MinDotWidth) and (DotHeight > MinDotHeight));
  603.     for Pos := 0 to HandPositions - 1 do begin
  604.       R.Top := (CircleTab^[Pos].Y * FClockRadius) div CirTabScale + FClockCenter.Y;
  605.       R.Left := (CircleTab^[Pos].X * FClockRadius) div CirTabScale + FClockCenter.X;
  606.       if (Pos mod 5) <> 0 then begin
  607.         if MinDots then begin
  608.           if Ctl3D then begin
  609.             Canvas.Brush.Color := clBtnShadow;
  610.             OffsetRect(R, -1, -1);
  611.             R.Right := R.Left + 2;
  612.             R.Bottom := R.Top + 2;
  613.             Canvas.FillRect(R);
  614.             Canvas.Brush.Color := clBtnHighlight;
  615.             OffsetRect(R, 1, 1);
  616.             Canvas.FillRect(R);
  617.             Canvas.Brush.Color := Self.Color;
  618.           end;
  619.           R.Right := R.Left + 1;
  620.           R.Bottom := R.Top + 1;
  621.           Canvas.FillRect(R);
  622.         end;
  623.       end
  624.       else begin
  625.         R.Right := R.Left + DotWidth;
  626.         R.Bottom := R.Top + DotHeight;
  627.         OffsetRect(R, -DotCenter.X, -DotCenter.Y);
  628.         if Ctl3D and MinDots then
  629.           with Canvas do begin
  630.             Brush.Color := FDotsColor;
  631.             Brush.Style := bsSolid;
  632.             FillRect(R);
  633.             Frame3D(Canvas, R, LightColor(FDotsColor), clWindowFrame, 1);
  634.           end;
  635.         Canvas.Brush.Color := Canvas.Pen.Color;
  636.         if not (Ctl3D and MinDots) then Canvas.FillRect(R);
  637.       end;
  638.     end;
  639.   finally
  640.     Canvas.Brush.Color := SaveBrush;
  641.     Canvas.Pen.Color := SavePen;
  642.   end;
  643. end;
  644. procedure TRxClock.CircleClock(MaxWidth, MaxHeight: Integer);
  645. var
  646.   ClockHeight: Integer;
  647.   ClockWidth: Integer;
  648. begin
  649.   if MaxWidth > HorzEquiv(MaxHeight) then begin
  650.     ClockWidth := HorzEquiv(MaxHeight);
  651.     FClockRect.Left := FClockRect.Left + ((MaxWidth - ClockWidth) div 2);
  652.     FClockRect.Right := FClockRect.Left + ClockWidth;
  653.   end
  654.   else begin
  655.     ClockHeight := VertEquiv(MaxWidth);
  656.     FClockRect.Top := FClockRect.Top + ((MaxHeight - ClockHeight) div 2);
  657.     FClockRect.Bottom := FClockRect.Top + ClockHeight;
  658.   end;
  659. end;
  660. procedure TRxClock.DrawSecondHand(Pos: Integer);
  661. var
  662.   Radius: Longint;
  663.   SaveMode: TPenMode;
  664. begin
  665.   Radius := (FClockRadius * SecondTip) div 100;
  666.   SaveMode := Canvas.Pen.Mode;
  667.   Canvas.Pen.Mode := pmNot;
  668.   try
  669.     Canvas.MoveTo(FClockCenter.X, FClockCenter.Y);
  670.     Canvas.LineTo(FClockCenter.X + ((CircleTab^[Pos].X * Radius) div
  671.       CirTabScale), FClockCenter.Y + ((CircleTab^[Pos].Y * Radius) div
  672.       CirTabScale));
  673.   finally
  674.     Canvas.Pen.Mode := SaveMode;
  675.   end;
  676. end;
  677. procedure TRxClock.DrawFatHand(Pos: Integer; HourHand: Boolean);
  678. var
  679.   ptSide, ptTail, ptTip: TPoint;
  680.   Index, Hand: Integer;
  681.   Scale: Longint;
  682.   SaveMode: TPenMode;
  683. begin
  684.   if HourHand then Hand := HourSide else Hand := MinuteSide;
  685.   Scale := (FClockRadius * Hand) div 100;
  686.   Index := (Pos + SideShift) mod HandPositions;
  687.   ptSide.Y := (CircleTab^[Index].Y * Scale) div CirTabScale;
  688.   ptSide.X := (CircleTab^[Index].X * Scale) div CirTabScale;
  689.   if HourHand then Hand := HourTip else Hand := MinuteTip;
  690.   Scale := (FClockRadius * Hand) div 100;
  691.   ptTip.Y := (CircleTab^[Pos].Y * Scale) div CirTabScale;
  692.   ptTip.X := (CircleTab^[Pos].X * Scale) div CirTabScale;
  693.   if HourHand then Hand := HourTail else Hand := MinuteTail;
  694.   Scale := (FClockRadius * Hand) div 100;
  695.   Index := (Pos + TailShift) mod HandPositions;
  696.   ptTail.Y := (CircleTab^[Index].Y * Scale) div CirTabScale;
  697.   ptTail.X := (CircleTab^[Index].X * Scale) div CirTabScale;
  698.   with Canvas do begin
  699.     SaveMode := Pen.Mode;
  700.     Pen.Mode := pmCopy;
  701.     try
  702.       MoveTo(FClockCenter.X + ptSide.X, FClockCenter.Y + ptSide.Y);
  703.       LineTo(FClockCenter.X + ptTip.X, FClockCenter.Y + ptTip.Y);
  704.       MoveTo(FClockCenter.X - ptSide.X, FClockCenter.Y - ptSide.Y);
  705.       LineTo(FClockCenter.X + ptTip.X, FClockCenter.Y + ptTip.Y);
  706.       MoveTo(FClockCenter.X + ptSide.X, FClockCenter.Y + ptSide.Y);
  707.       LineTo(FClockCenter.X + ptTail.X, FClockCenter.Y + ptTail.Y);
  708.       MoveTo(FClockCenter.X - ptSide.X, FClockCenter.Y - ptSide.Y);
  709.       LineTo(FClockCenter.X + ptTail.X, FClockCenter.Y + ptTail.Y);
  710.     finally
  711.       Pen.Mode := SaveMode;
  712.     end;
  713.   end;
  714. end;
  715. procedure TRxClock.PaintAnalogClock(PaintMode: TPaintMode);
  716. var
  717.   NewTime: TRxClockTime;
  718. begin
  719.   Canvas.Pen.Color := Font.Color;
  720.   Canvas.Brush.Color := Color;
  721.   SetBkMode(Canvas.Handle, TRANSPARENT);
  722.   if PaintMode = pmPaintAll then begin
  723.     with Canvas do begin
  724.       FillRect(FClockRect);
  725.       Pen.Color := Self.Font.Color;
  726.       DrawAnalogFace;
  727.       DrawFatHand(HourHandPos(FDisplayTime), True);
  728.       DrawFatHand(FDisplayTime.Minute, False);
  729.       Pen.Color := Brush.Color;
  730.       if ShowSeconds then DrawSecondHand(FDisplayTime.Second);
  731.     end;
  732.   end
  733.   else begin
  734.     with Canvas do begin
  735.       Pen.Color := Brush.Color;
  736.       GetTime(NewTime);
  737.       if NewTime.Hour >= 12 then Dec(NewTime.Hour, 12);
  738.       if (NewTime.Second <> FDisplayTime.Second) then
  739.         if ShowSeconds then DrawSecondHand(FDisplayTime.Second);
  740.       if ((NewTime.Minute <> FDisplayTime.Minute) or
  741.         (NewTime.Hour <> FDisplayTime.Hour)) then
  742.       begin
  743.         DrawFatHand(FDisplayTime.Minute, False);
  744.         DrawFatHand(HourHandPos(FDisplayTime), True);
  745.         Pen.Color := Self.Font.Color;
  746.         DrawFatHand(NewTime.Minute, False);
  747.         DrawFatHand(HourHandPos(NewTime), True);
  748.       end;
  749.       Pen.Color := Brush.Color;
  750.       if (NewTime.Second <> FDisplayTime.Second) then begin
  751.         if ShowSeconds then DrawSecondHand(NewTime.Second);
  752.         FDisplayTime := NewTime;
  753.       end;
  754.     end;
  755.   end;
  756. end;
  757. procedure TRxClock.PaintTimeStr(var Rect: TRect; FullTime: Boolean);
  758. var
  759.   FontHeight, FontWidth, FullWidth, I, L, H: Integer;
  760.   TimeStr, SAmPm: string;
  761.   NewTime: TRxClockTime;
  762.   function IsPartSym(Idx, Num: Byte): Boolean;
  763.   var
  764.     TwoSymHour: Boolean;
  765.   begin
  766.     TwoSymHour := (H >= 10) or FLeadingZero;
  767.     case Idx of
  768.       1: begin {hours}
  769.            Result := True;
  770.          end;
  771.       2: begin {minutes}
  772.            if TwoSymHour then Result := (Num in [4, 5])
  773.            else Result := (Num in [3, 4]);
  774.          end;
  775.       3: begin {seconds}
  776.            if TwoSymHour then Result := FShowSeconds and (Num in [7, 8])
  777.            else Result := FShowSeconds and (Num in [6, 7]);
  778.          end;
  779.       else Result := False;
  780.     end;
  781.   end;
  782.   procedure DrawSym(Sym: Char; Num: Byte);
  783.   begin
  784.     if FullTime or
  785.       ((NewTime.Second <> FDisplayTime.Second) and IsPartSym(3, Num)) or
  786.       ((NewTime.Minute <> FDisplayTime.Minute) and IsPartSym(2, Num)) or
  787.       (NewTime.Hour <> FDisplayTime.Hour) then
  788.     begin
  789.       Canvas.FillRect(Rect);
  790.       DrawText(Canvas.Handle, @Sym, 1, Rect, DT_EXPANDTABS or
  791.         DT_VCENTER or DT_CENTER or DT_NOCLIP or DT_SINGLELINE);
  792.     end;
  793.   end;
  794. begin
  795.   GetTime(NewTime);
  796.   H := NewTime.Hour;
  797.   if NewTime.Hour >= 12 then Dec(NewTime.Hour, 12);
  798.   if FTwelveHour then begin
  799.     if H > 12 then Dec(H, 12) else if H = 0 then H := 12;
  800.   end;
  801.   if (not FullTime) and (NewTime.Hour <> FDisplayTime.Hour) then begin
  802.     Repaint;
  803.     Exit;
  804.   end;
  805.   if FLeadingZero then TimeStr := 'hh:mm' else TimeStr := 'h:mm';
  806.   if FShowSeconds then TimeStr := TimeStr + ':ss';
  807.   if FTwelveHour then TimeStr := TimeStr + ' ampm';
  808.   with NewTime do
  809.     TimeStr := FormatDateTime(TimeStr, GetSystemTime);
  810.   if (H >= 10) or FLeadingZero then L := 5 else L := 4;
  811.   if FShowSeconds then Inc(L, 3);
  812.   SAmPm := Copy(TimeStr, L + 1, MaxInt);
  813.   with Canvas do begin
  814.     Font := Self.Font;
  815.     FontHeight := TextHeight('8');
  816.     FontWidth := TextWidth('8');
  817.     FullWidth := TextWidth(SAmPm) + (L * FontWidth);
  818.     with Rect do begin
  819.       Left := ((Right + Left) - FullWidth) div 2 {shr 1};
  820.       Right := Left + FullWidth;
  821.       Top := ((Bottom + Top) - FontHeight) div 2 {shr 1};
  822.       Bottom := Top + FontHeight;
  823.     end;
  824.     Brush.Color := Color;
  825.     for I := 1 to L do begin
  826.       Rect.Right := Rect.Left + FontWidth;
  827.       DrawSym(TimeStr[I], I);
  828.       Inc(Rect.Left, FontWidth);
  829.     end;
  830.     if FullTime or (NewTime.Hour <> FDisplayTime.Hour) then begin
  831.       Rect.Right := Rect.Left + TextWidth(SAmPm);
  832.       DrawText(Handle, @SAmPm[1], Length(SAmPm), Rect,
  833.         DT_EXPANDTABS or DT_VCENTER or DT_NOCLIP or DT_SINGLELINE);
  834.     end;
  835.   end;
  836.   FDisplayTime := NewTime;
  837. end;
  838. procedure TRxClock.Paint3DFrame(var Rect: TRect);
  839. var
  840.   TopColor, BottomColor: TColor;
  841.   procedure AdjustColors(Bevel: TPanelBevel);
  842.   begin
  843.     TopColor := clBtnHighlight;
  844.     if Bevel = bvLowered then TopColor := clBtnShadow;
  845.     BottomColor := clBtnShadow;
  846.     if Bevel = bvLowered then BottomColor := clBtnHighlight;
  847.   end;
  848. begin
  849.   Rect := GetClientRect;
  850.   with Canvas do begin
  851.     Brush.Color := Color;
  852.     FillRect(Rect);
  853.   end;
  854.   if BevelOuter <> bvNone then begin
  855.     AdjustColors(BevelOuter);
  856.     Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
  857.   end;
  858.   InflateRect(Rect, -BorderWidth, -BorderWidth);
  859.   if BevelInner <> bvNone then begin
  860.     AdjustColors(BevelInner);
  861.     Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
  862.   end;
  863. end;
  864. procedure TRxClock.Paint;
  865. var
  866.   R: TRect;
  867. begin
  868.   Paint3DFrame(R);
  869.   case FShowMode of
  870.     scDigital: PaintTimeStr(R, True);
  871.     scAnalog: PaintAnalogClock(pmPaintAll);
  872.   end;
  873. end;
  874. end.