994 lines
29 KiB
ObjectPascal
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.
|