{*******************************************************} { } { Delphi VCL Extensions (RX) } { } { Copyright (c) 1995, 1996 AO ROSNO } { Copyright (c) 1997, 1998 Master-Bank } { } {*******************************************************} unit rxclock; interface {$I rx.inc} uses LCLType, LMessages, SysUtils, Classes, Graphics, Controls, Forms, StdCtrls, ExtCtrls, Menus, messages; 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: TTimer; 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: TLMessage): Boolean; procedure CMCtl3DChanged(var Message: TLMessage); message CM_CTL3DCHANGED; procedure CMTextChanged(var Message: TLMessage); message CM_TEXTCHANGED; procedure CMFontChanged(var Message: TLMessage); message CM_FONTCHANGED; {$IFDEF windows} procedure WMTimeChange(var Message: TLMessage); message WM_TIMECHANGE; {$ENDIF} protected { Protected declarations } procedure SetAutoSize(const Value: Boolean); virtual; 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; property OnContextPopup; property OnStartDrag; // property OnCanResize; property OnConstrainedResize; property OnDockDrop; property OnDockOver; property OnEndDock; // property OnGetSiteInfo; // property OnStartDock; // property OnUnDock; end; implementation uses VCLUtils, RTLConsts{, LConsts}, LCLIntf; 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(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} Font.Height := -fHeight; NewH := MulDiv(fHeight, MaxW, Canvas.TextWidth(Text)); if NewH > MaxH then NewH := MaxH; Font.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] - [csReplicatable]; BevelInner := bvLowered; BevelOuter := bvRaised; FTimer := TTimer.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; {$IFDEF windows} procedure TRxClock.WMTimeChange(var Message: TMessage); begin inherited; Invalidate; CheckAlarm; end; {$ENDIF} function TRxClock.FormatSettingsChange(var Message: TMessage): Boolean; begin {$IFDEF windows} Result := False; case Message.Msg of WM_WININICHANGE: begin Invalidate; if AutoSize then Realign; end; end; {$ENDIF} 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 Canvas.Handle := 0; ReleaseDC(0, DC); end; end; procedure TRxClock.AlignControls(AControl: TControl; var Rect: TRect); {$IFDEF RX_D4} var InflateWidth: Integer; {$ENDIF} begin inherited AlignControls(AControl, Rect); FClockRect := Rect; {$IFDEF RX_D4} InflateWidth := BorderWidth + 1; if BevelOuter <> bvNone then Inc(InflateWidth, BevelWidth); if BevelInner <> bvNone then Inc(InflateWidth, BevelWidth); InflateRect(FClockRect, -InflateWidth, -InflateWidth); {$ENDIF} with FClockRect do CircleClock(Right - Left, Bottom - Top); if AutoSize then ResizeFont(Rect); end; procedure TRxClock.Alarm; begin if Assigned(FOnAlarm) then FOnAlarm(Self); end; procedure TRxClock.SetAutoSize(const Value: Boolean); begin if (Value <> FAutoSize) then begin FAutoSize := Value; if FAutoSize then begin Invalidate; Realign; end; end; end; procedure TRxClock.SetTwelveHour(Value: Boolean); begin if FTwelveHour <> Value then begin FTwelveHour := Value; Invalidate; if AutoSize then Realign; end; end; procedure TRxClock.SetLeadingZero(Value: Boolean); begin if FLeadingZero <> Value then begin FLeadingZero := Value; Invalidate; end; end; procedure TRxClock.SetShowSeconds(Value: Boolean); begin if FShowSeconds <> Value then begin {if FShowSeconds and (ShowMode = scAnalog) then DrawSecondHand(FDisplayTime.Second);} FShowSeconds := Value; Invalidate; if AutoSize then Realign; end; end; procedure TRxClock.SetDotsColor(Value: TColor); begin if Value <> FDotsColor then begin FDotsColor := Value; Invalidate; end; end; procedure TRxClock.SetShowMode(Value: TShowClock); begin if FShowMode <> Value then begin FShowMode := Value; Invalidate; end; end; function TRxClock.GetAlarmElement(Index: Integer): Byte; var Hour, Min, Sec, MSec: Word; begin DecodeTime(FAlarm, Hour, Min, Sec, MSec); case Index of 1: Result := Hour; 2: Result := Min; 3: Result := Sec; else Result := 0; end; end; procedure TRxClock.SetAlarmElement(Index: Integer; Value: Byte); var Hour, Min, Sec, MSec: Word; begin DecodeTime(FAlarm, Hour, Min, Sec, MSec); case Index of 1: Hour := Value; 2: Min := Value; 3: Sec := Value; else Exit; end; if (Hour < 24) and (Min < 60) and (Sec < 60) then begin FAlarm := EncodeTime(Hour, Min, Sec, 0); ResetAlarm; end else InvalidTime(Hour, Min, Sec); end; procedure TRxClock.SetAlarmTime(AlarmTime: TDateTime); var Hour, Min, Sec, MSec: Word; begin DecodeTime(FAlarm, Hour, Min, Sec, MSec); if (Hour < 24) and (Min < 60) and (Sec < 60) then begin FAlarm := Frac(AlarmTime); ResetAlarm; end else InvalidTime(Hour, Min, Sec); end; procedure TRxClock.TimerExpired(Sender: TObject); var DC: HDC; Rect: TRect; InflateWidth: Integer; begin DC := GetDC(Handle); try Canvas.Handle := DC; Canvas.Brush.Color := Color; Canvas.Font := Font; Canvas.Pen.Color := Font.Color; if FShowMode = scAnalog then PaintAnalogClock(pmHandPaint) else begin Rect := GetClientRect; InflateWidth := BorderWidth; if BevelOuter <> bvNone then Inc(InflateWidth, BevelWidth); if BevelInner <> bvNone then Inc(InflateWidth, BevelWidth); InflateRect(Rect, -InflateWidth, -InflateWidth); PaintTimeStr(Rect, False); end; finally Canvas.Handle := 0; ReleaseDC(Handle, DC); end; CheckAlarm; end; procedure TRxClock.CheckAlarm; begin if FAlarmEnabled and IsAlarmTime(GetSystemTime) then begin if FAlarmWait then begin FAlarmWait := False; Alarm; end; end else ResetAlarm; end; procedure TRxClock.DrawAnalogFace; var Pos, DotHeight, DotWidth: Integer; DotCenter: TPoint; R: TRect; SaveBrush, SavePen: TColor; MinDots: Boolean; begin DotWidth := (MaxDotWidth * Longint(FClockRect.Right - FClockRect.Left)) div HRes; DotHeight := VertEquiv(DotWidth); if DotHeight < MinDotHeight then DotHeight := MinDotHeight; if DotWidth < MinDotWidth then DotWidth := MinDotWidth; DotCenter.X := DotWidth div 2; DotCenter.Y := DotHeight div 2; InflateRect(FClockRect, -DotCenter.Y, -DotCenter.X); FClockRadius := ((FClockRect.Right - FClockRect.Left) div 2); FClockCenter.X := FClockRect.Left + FClockRadius; FClockCenter.Y := FClockRect.Top + ((FClockRect.Bottom - FClockRect.Top) div 2); InflateRect(FClockRect, DotCenter.Y, DotCenter.X); SaveBrush := Canvas.Brush.Color; SavePen := Canvas.Pen.Color; try Canvas.Brush.Color := Canvas.Pen.Color; MinDots := ((DotWidth > MinDotWidth) and (DotHeight > MinDotHeight)); for Pos := 0 to HandPositions - 1 do begin R.Top := (CircleTab^[Pos].Y * FClockRadius) div CirTabScale + FClockCenter.Y; R.Left := (CircleTab^[Pos].X * FClockRadius) div CirTabScale + FClockCenter.X; if (Pos mod 5) <> 0 then begin if MinDots then begin if Ctl3D then begin Canvas.Brush.Color := clBtnShadow; OffsetRect(R, -1, -1); R.Right := R.Left + 2; R.Bottom := R.Top + 2; Canvas.FillRect(R); Canvas.Brush.Color := clBtnHighlight; OffsetRect(R, 1, 1); Canvas.FillRect(R); Canvas.Brush.Color := Self.Color; end; R.Right := R.Left + 1; R.Bottom := R.Top + 1; Canvas.FillRect(R); end; end else begin R.Right := R.Left + DotWidth; R.Bottom := R.Top + DotHeight; OffsetRect(R, -DotCenter.X, -DotCenter.Y); if Ctl3D and MinDots then with Canvas do begin Brush.Color := FDotsColor; Brush.Style := bsSolid; FillRect(R); RxFrame3D(Canvas, R, LightColor(FDotsColor), clWindowFrame, 1); end; Canvas.Brush.Color := Canvas.Pen.Color; if not (Ctl3D and MinDots) then Canvas.FillRect(R); end; end; finally Canvas.Brush.Color := SaveBrush; Canvas.Pen.Color := SavePen; end; end; procedure TRxClock.CircleClock(MaxWidth, MaxHeight: Integer); var ClockHeight: Integer; ClockWidth: Integer; begin if MaxWidth > HorzEquiv(MaxHeight) then begin ClockWidth := HorzEquiv(MaxHeight); FClockRect.Left := FClockRect.Left + ((MaxWidth - ClockWidth) div 2); FClockRect.Right := FClockRect.Left + ClockWidth; end else begin ClockHeight := VertEquiv(MaxWidth); FClockRect.Top := FClockRect.Top + ((MaxHeight - ClockHeight) div 2); FClockRect.Bottom := FClockRect.Top + ClockHeight; end; end; procedure TRxClock.DrawSecondHand(Pos: Integer); var Radius: Longint; SaveMode: TPenMode; begin Radius := (FClockRadius * SecondTip) div 100; SaveMode := Canvas.Pen.Mode; Canvas.Pen.Mode := pmNot; try Canvas.MoveTo(FClockCenter.X, FClockCenter.Y); Canvas.LineTo(FClockCenter.X + ((CircleTab^[Pos].X * Radius) div CirTabScale), FClockCenter.Y + ((CircleTab^[Pos].Y * Radius) div CirTabScale)); finally Canvas.Pen.Mode := SaveMode; end; end; procedure TRxClock.DrawFatHand(Pos: Integer; HourHand: Boolean); var ptSide, ptTail, ptTip: TPoint; Index, Hand: Integer; Scale: Longint; SaveMode: TPenMode; begin if HourHand then Hand := HourSide else Hand := MinuteSide; Scale := (FClockRadius * Hand) div 100; Index := (Pos + SideShift) mod HandPositions; ptSide.Y := (CircleTab^[Index].Y * Scale) div CirTabScale; ptSide.X := (CircleTab^[Index].X * Scale) div CirTabScale; if HourHand then Hand := HourTip else Hand := MinuteTip; Scale := (FClockRadius * Hand) div 100; ptTip.Y := (CircleTab^[Pos].Y * Scale) div CirTabScale; ptTip.X := (CircleTab^[Pos].X * Scale) div CirTabScale; if HourHand then Hand := HourTail else Hand := MinuteTail; Scale := (FClockRadius * Hand) div 100; Index := (Pos + TailShift) mod HandPositions; ptTail.Y := (CircleTab^[Index].Y * Scale) div CirTabScale; ptTail.X := (CircleTab^[Index].X * Scale) div CirTabScale; with Canvas do begin SaveMode := Pen.Mode; Pen.Mode := pmCopy; try MoveTo(FClockCenter.X + ptSide.X, FClockCenter.Y + ptSide.Y); LineTo(FClockCenter.X + ptTip.X, FClockCenter.Y + ptTip.Y); MoveTo(FClockCenter.X - ptSide.X, FClockCenter.Y - ptSide.Y); LineTo(FClockCenter.X + ptTip.X, FClockCenter.Y + ptTip.Y); MoveTo(FClockCenter.X + ptSide.X, FClockCenter.Y + ptSide.Y); LineTo(FClockCenter.X + ptTail.X, FClockCenter.Y + ptTail.Y); MoveTo(FClockCenter.X - ptSide.X, FClockCenter.Y - ptSide.Y); LineTo(FClockCenter.X + ptTail.X, FClockCenter.Y + ptTail.Y); finally Pen.Mode := SaveMode; end; end; end; procedure TRxClock.PaintAnalogClock(PaintMode: TPaintMode); var NewTime: TRxClockTime; begin Canvas.Pen.Color := Font.Color; Canvas.Brush.Color := Color; SetBkMode(Canvas.Handle, TRANSPARENT); if PaintMode = pmPaintAll then begin with Canvas do begin FillRect(FClockRect); Pen.Color := Self.Font.Color; DrawAnalogFace; DrawFatHand(HourHandPos(FDisplayTime), True); DrawFatHand(FDisplayTime.Minute, False); Pen.Color := Brush.Color; if ShowSeconds then DrawSecondHand(FDisplayTime.Second); end; end else begin with Canvas do begin Pen.Color := Brush.Color; GetTime(NewTime); if NewTime.Hour >= 12 then Dec(NewTime.Hour, 12); if (NewTime.Second <> FDisplayTime.Second) then if ShowSeconds then DrawSecondHand(FDisplayTime.Second); if ((NewTime.Minute <> FDisplayTime.Minute) or (NewTime.Hour <> FDisplayTime.Hour)) then begin DrawFatHand(FDisplayTime.Minute, False); DrawFatHand(HourHandPos(FDisplayTime), True); Pen.Color := Self.Font.Color; DrawFatHand(NewTime.Minute, False); DrawFatHand(HourHandPos(NewTime), True); end; Pen.Color := Brush.Color; if (NewTime.Second <> FDisplayTime.Second) then begin if ShowSeconds then DrawSecondHand(NewTime.Second); FDisplayTime := NewTime; end; end; end; end; procedure TRxClock.PaintTimeStr(var Rect: TRect; FullTime: Boolean); var FontHeight, FontWidth, FullWidth, I, L, H: Integer; TimeStr, SAmPm: string; NewTime: TRxClockTime; function IsPartSym(Idx, Num: Byte): Boolean; var TwoSymHour: Boolean; begin TwoSymHour := (H >= 10) or FLeadingZero; case Idx of 1: begin {hours} Result := True; end; 2: begin {minutes} if TwoSymHour then Result := (Num in [4, 5]) else Result := (Num in [3, 4]); end; 3: begin {seconds} if TwoSymHour then Result := FShowSeconds and (Num in [7, 8]) else Result := FShowSeconds and (Num in [6, 7]); end; else Result := False; end; end; procedure DrawSym(Sym: Char; Num: Byte); begin if FullTime or ((NewTime.Second <> FDisplayTime.Second) and IsPartSym(3, Num)) or ((NewTime.Minute <> FDisplayTime.Minute) and IsPartSym(2, Num)) or (NewTime.Hour <> FDisplayTime.Hour) then begin Canvas.FillRect(Rect); DrawText(Canvas.Handle, @Sym, 1, Rect, DT_EXPANDTABS or DT_VCENTER or DT_CENTER or DT_NOCLIP or DT_SINGLELINE); end; end; begin GetTime(NewTime); H := NewTime.Hour; if NewTime.Hour >= 12 then Dec(NewTime.Hour, 12); if FTwelveHour then begin if H > 12 then Dec(H, 12) else if H = 0 then H := 12; end; if (not FullTime) and (NewTime.Hour <> FDisplayTime.Hour) then begin Repaint; Exit; end; if FLeadingZero then TimeStr := 'hh:mm' else TimeStr := 'h:mm'; if FShowSeconds then TimeStr := TimeStr + ':ss'; if FTwelveHour then TimeStr := TimeStr + ' ampm'; with NewTime do TimeStr := FormatDateTime(TimeStr, GetSystemTime); if (H >= 10) or FLeadingZero then L := 5 else L := 4; if FShowSeconds then Inc(L, 3); SAmPm := Copy(TimeStr, L + 1, MaxInt); with Canvas do begin Font := Self.Font; FontHeight := TextHeight('8'); FontWidth := TextWidth('8'); FullWidth := TextWidth(SAmPm) + (L * FontWidth); with Rect do begin Left := ((Right + Left) - FullWidth) div 2 {shr 1}; Right := Left + FullWidth; Top := ((Bottom + Top) - FontHeight) div 2 {shr 1}; Bottom := Top + FontHeight; end; Brush.Color := Color; for I := 1 to L do begin Rect.Right := Rect.Left + FontWidth; DrawSym(TimeStr[I], I); Inc(Rect.Left, FontWidth); end; if FullTime or (NewTime.Hour <> FDisplayTime.Hour) then begin Rect.Right := Rect.Left + TextWidth(SAmPm); DrawText(Handle, @SAmPm[1], Length(SAmPm), Rect, DT_EXPANDTABS or DT_VCENTER or DT_NOCLIP or DT_SINGLELINE); end; end; FDisplayTime := NewTime; end; procedure TRxClock.Paint3DFrame(var Rect: TRect); var TopColor, BottomColor: TColor; procedure AdjustColors(Bevel: TPanelBevel); begin TopColor := clBtnHighlight; if Bevel = bvLowered then TopColor := clBtnShadow; BottomColor := clBtnShadow; if Bevel = bvLowered then BottomColor := clBtnHighlight; end; begin Rect := GetClientRect; with Canvas do begin Brush.Color := Color; FillRect(Rect); end; if BevelOuter <> bvNone then begin AdjustColors(BevelOuter); RxFrame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth); end; InflateRect(Rect, -BorderWidth, -BorderWidth); if BevelInner <> bvNone then begin AdjustColors(BevelInner); RxFrame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth); end; end; procedure TRxClock.Paint; var R: TRect; begin Paint3DFrame(R); case FShowMode of scDigital: PaintTimeStr(R, True); scAnalog: PaintAnalogClock(pmPaintAll); end; end; end.