994 lines
29 KiB
ObjectPascal

{ rxclock unit
Copyright (C) 2005-2017 Lagunov Aleksey alexs75@yandex.ru and Lazarus team
original conception from rx library for Delphi (c)
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version with the following modification:
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent modules,and
to copy and distribute the resulting executable under terms of your choice,
provided that you also meet, for each linked independent module, the terms
and conditions of the license of that module. An independent module is a
module which is not derived from or based on this library. If you modify
this library, you may extend this exception to your version of the library,
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
for more details.
You should have received a copy of the GNU Library General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
}
unit rxclock;
interface
{$I rx.inc}
uses LCLType, LMessages, SysUtils, Classes, Graphics, Controls, Forms,
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 Cursor;
property DragMode;
property DragCursor;
property Enabled;
property Font;
property ParentColor;
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 OnMouseWheel;
property OnMouseWheelDown;
property OnMouseWheelUp;
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 rxlclutils, RTLConsts, 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) + DefaultFormatSettings.TimeSeparator + IntToStr(Min) +
DefaultFormatSettings.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(DefaultFormatSettings.TimeAMString) > Canvas.TextWidth(DefaultFormatSettings.TimePMString) then
TimeStr := TimeStr + ' ' + DefaultFormatSettings.TimeAMString
else TimeStr := TimeStr + ' ' + DefaultFormatSettings.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.