home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 1999 January
/
Chip_1999-01_cd.bin
/
zkuste
/
delphi
/
D1
/
CALPNL.ZIP
/
Calpnl.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1996-03-13
|
36KB
|
1,106 lines
unit Calpnl;
{ Posted in the hope that I can repay a little of my enormous debt to
those many unselfish people who have made my life easier with freeware
and code snippets.
-o0o-
TCalenPnl, a freeware Calendar descended from TCustomPanel. The really
hard work for this component was done by Robert Vivrette, and is adapted
from his freeware TDateEdit form.
I needed a panel-based Calendar, and adapted the CalPop code to suit.
TCalenPnl retains all the properties of a TPanel, and adds a few more.
Some of the interesting published properties are...
ShowDate: Shows\Hides the buttons and 'MMMMM YYYY' display
above the abbreviated day names at the top. The Months
or Years can then be changed programmatically by
ScrollBars or similar.
DayWidth: Uses 1 to 3 characters (M, Mo, Mon) to define the day name.
Font: Big deal! Actually, the point is that the Font can be
changed (typically the size would be changed) when
TCalenPnl is Resized (OnResize).
OnDateChange: A centralized event that allows users to change Labels,
ScrollBars, Graphs or ProgressBars when the CalendarDate
property is changed, internally or externally.
Some interesting Public properties...
CalendarDate: A TDateTime property that you can read or write to
programmatically. The fractional part of CalendarDate,
i.e. the time, is not stored.
WeekNumber: An integer representing the... Week number of the TCalenPnl.Year.
DayOfYear: Integer value for days that have passed, in the current
(CalendarDate) year.
DaysInYear: Integer, can be either 365 or 366. It could have just as
easily been Boolean (it calls the Boolean IsLeapYear protected
Function), but it suited my project.
.Day, .Month, .Year are all integer Public Properties.
There is some repitition in the code, as Robert's CalPop relies on the date
being changed only by the buttons, therefore only in increments of one. I
required TCalenPnl to be able to be set by other controls, so there is some
duplication. A really clever programmer, over a rainy weekend, could re-do
the code to shrink it a touch.
You may have to look closely at some of the code, as it has been written to
prevent a user entering an invalid date, which can happen with a ScrollBar.
If the date highlighted is 31 August, and the user scrolls to September, the
CalendarDate.Day is reset to the DaysInMonth (ie, 30), to prevent an error.
Shouldn't be a problem as it almost guarantees no errors, but be aware.
If you use 'MMMM DD YYYY' format in your Win International settings, ie US
users, then the example above would use August 31. In other words, the code
is 'Internationalized', to that extent.
While CalPnl.PAS and the CalPnl.DCR have been produced in Delphi 2.0, there
is no reason why the .PAS would not work in 16 bit Delphi, apart from a few
// comments.
I considered a dynamic StartOfWeek, as some other calendar programmes offer,
because it is culturally presumptuous of me to use Sunday as day 1. If you
wish to modify the source, please do so, and send me a copy to re-post.
If you have any criticisms or suggestions, please send them to me...
Peter Crain
Brisbane, Queensland.
AUSTRALIA.
Compuserve 100237,2735
}
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, extctrls, Menus;
const
BORDER = 2;
DAYS_IN_MONTH: array[1..12] of Integer = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
BUTTON_WIDTH = 16;
type
TDayWidth = (dw1Char, dw2Char, dw3Char);
TPaintJob = (All, Header, Dates);
type
TDateType = record
aYear, aMonth, aDay : Word;
End; {Record}
type
TCalenPnl = class(TCustomPanel)
private
g_MouseDown : BOOL;
g_PrevYear, g_PrevMonth : Word;
g_DateArray : array[1..42] of string[2];
g_CurrDateIndex : Integer;
g_PrevDateIndex : Integer;
g_DayTitles : Array[0..6] of string[3]; {moved from const to enable Int ShortDayNames}
FOnDblClick: TNotifyEvent;
FOnDateChange: TNotifyEvent;
FButton: TMouseButton;
FButtonDown: Boolean;
FShowDate: Boolean;
FUseLongDate: Boolean;
g_RectHeight: Integer;
g_Width: Integer;
HeadingRect: TRect;
CalendarRect : TRect;
FMonth: Integer;
FDay: Integer;
FYear: Integer;
FDayWidth: TDayWidth;
FCalendarDate: TDateTime;
FWeekNumber: Integer;
FDayOfYear: Integer;
FDaysInYear: Integer;
procedure SetCalendarDate(aDate: TDateTime);
procedure SetMonth(Value: Integer);
procedure SetDay(Value: Integer);
procedure SetYear(Value: Integer);
function GetShowDate: Boolean;
procedure SetShowDate(Value: Boolean);
procedure SetDayWidth(Value: TDayWidth);
function GetUseLongDate: Boolean;
procedure SetUseLongDate(Value: Boolean);
function JulDate1stWeek(JD : TDateTime) : TDateTime;
function WeekNo(JDate : TDateTime): Integer;
function GetWeekNumber: Integer;
function DOY (y, m, d : Word): Integer;
function GetDayOfYear: Integer;
function GetDaysInYear: integer;
protected
procedure Paint; override;
procedure DateChange;
procedure DrawMonthHeader;
procedure DrawDaysHeader;
procedure DrawDates;
procedure DrawFocusFrame(nIndex : Integer);
procedure LoadDateArray;
function GetMonthBegin: Integer;
function DaysInMonth(nMonth, nYear : Integer): Integer;
function IsLeapYear(AYear: Integer): Boolean;
function SetDate(nDays : Integer): Boolean;
function GetLeftButtonRect : TRect;
function GetRightButtonRect : TRect;
function GetRectFromIndex(nIndex : Integer): TRect;
function GetIndexFromDate : Integer;
function GetIndexFromPoint(nLeft : Integer ; nTop : Integer) : Integer;
procedure DrawButtons;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
function ValidDate(aDate: TDateType) : Boolean;
public
constructor Create(AOwner: TComponent); override;
property Day: Integer read FDay write SetDay;
property Month: Integer read FMonth write SetMonth;
property Year: Integer read FYear write SetYear;
property CalendarDate: TDateTime read FCalendarDate write SetCalendarDate;
property WeekNumber: Integer read GetWeekNumber;
property DayOfYear: Integer read GetDayOfYear;
property DaysInYear: Integer read GetDaysInYear;
published
property Align;
property BevelInner default bvLowered;
property BevelOuter default bvRaised;
property BevelWidth default 1;
property BorderStyle default bsNone;
property BorderWidth default 1;
property Color;
property Ctl3D;
property Cursor;
property DragCursor;
property DragMode;
property Enabled;
property Font;
property Height default 160;
property HelpContext;
property Hint;
property Left;
property Locked;
property Name;
property ParentColor;
property ParentCtl3D;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property TabOrder;
property TabStop;
property Tag;
property Top;
property Visible;
property Width default 160;
property ShowDate: Boolean read GetShowDate write SetShowDate default True;
property UseLongDate: Boolean read GetUseLongDate write SetUseLongDate; {defaults to False}
property DayWidth: TDayWidth read FDayWidth write SetDayWidth default dw3Char;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnResize;
property OnDateChange: TNotifyEvent read FOnDateChange write FOnDateChange;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TCalenPnl]);
end;
function PointInRect( const rectTest: TRect; X, Y: integer ): boolean;
begin
Result := ( ( X >= rectTest.Left ) and ( X <= rectTest.Right ) and
( Y >= rectTest.Top ) and ( Y <= rectTest.Bottom ) );
end;
function TCalenPnl.GetShowDate: Boolean;
begin
Result := FShowDate;
end;
procedure TCalenPnl.SetShowDate(Value: Boolean);
begin
if Value <> FShowDate then
begin FShowDate := Value;
Refresh;
end;
end;
function TCalenPnl.GetUseLongDate: Boolean;
begin
Result := FUseLongDate;
end;
procedure TCalenPnl.SetUseLongDate(Value: Boolean);
begin
if Value <> FUseLongDate then
begin FUseLongDate := Value;
Refresh;
end;
end;
procedure TCalenPnl.SetDayWidth(Value: TDayWidth);
begin
if Value <> FDayWidth then
begin FDayWidth := Value;
Refresh;
end;
end;
constructor TCalenPnl.Create(AOwner: TComponent);
var
iCount: Integer;
aY, aM, aD: Word;
begin
inherited Create(AOwner);
Height := 160;
Width := 160;
BevelOuter := bvRaised;
BevelInner := bvLowered;
BevelWidth := 1;
BorderStyle := bsNone;
BorderWidth := 1;
DayWidth := dw3Char;
for iCount := 0 to 6 do g_DayTitles[iCount] := ShortDayNames[iCount +1];
FCalendarDate := Date;
FShowDate := True;
DecodeDate(FCalendarDate, aY, aM, aD );
FMonth := Integer(aM);
FDay := Integer(aD);
FYear := Integer(aY);
g_PrevDateIndex := 0;
LoadDateArray;
SetDate(0);
g_MouseDown := False;
end;
procedure TCalenPnl.Paint;
var
iInnerSpace, iWBorder, iHBorder, iInnerW, innerH, iLMargin, iLinesH: Integer;
begin
inherited Paint;
iInnerSpace := 0;
if BorderStyle = bsSingle then iInnerSpace := 1;
if BevelOuter <> bvNone then iInnerSpace := BevelWidth + iInnerSpace;
if BevelInner <> bvNone then iInnerSpace:= BevelWidth + iInnerSpace; { + 1}
iInnerSpace:= BorderWidth + iInnerSpace;
{iInnerSpace = the border, including bevels, on 1 side}
iInnerW := Width - (iInnerSpace * 2);
iWBorder := iInnerW div 100;
{g_Width is a product of useable space, not all space}
{clear space less a border both sides, makes g_Width narrower}
g_Width := (iInnerW - (iWBorder * 2)) div 7;
innerH := Height - (iInnerSpace * 2);
iHBorder := innerH div 100;
if ShowDate then iLinesH := 8 else iLinesH := 7;
{take out 2 iHBorder for spacing at top}
g_RectHeight := (innerH - (iHBorder * 2) ) div iLinesH;
iLMargin := (iInnerW - (g_Width * 7)) div 2;
HeadingRect := ClientRect;
HeadingRect.Top := HeadingRect.Top + iInnerSpace + iHBorder;
HeadingRect.Left := HeadingRect.Left + iInnerSpace + iLMargin ;
HeadingRect.Right := HeadingRect.Left + (g_Width * 7) ;
if ShowDate then HeadingRect.Bottom := HeadingRect.Top + (g_RectHeight * 2)
else HeadingRect.Bottom := HeadingRect.Top + g_RectHeight;
CalendarRect := HeadingRect;
CalendarRect.Top := HeadingRect.Bottom ;
CalendarRect.Bottom := CalendarRect.Top + (g_RectHeight * 6);
Canvas.Brush.Color := clBtnFace;
Canvas.FillRect(CalendarRect);
g_CurrDateIndex := FDay + GetMonthBegin - 1;
if ShowDate then
begin
DrawButtons;
DrawMonthHeader;
end;
DrawDaysHeader;
DrawDates;
DrawFocusFrame(g_CurrDateIndex);
end;
procedure TCalenPnl.DrawMonthHeader;
var
iRectHt, iSpaces, iIndent: Integer;
sMonth : String;
pMonth : PChar;
TempRect : TRect;
begin
with Canvas do
begin
Font.Color := clBlack;
Font.Style := [fsBold];
if UseLongDate then sMonth := FormatDateTime( 'mmmm yyyy', FCalendarDate )
else sMonth := FormatDateTime( 'mmm yyyy', FCalendarDate );
pMonth := StrAlloc( Length( sMonth ) + BORDER );
StrPCopy( pMonth, sMonth );
TempRect := HeadingRect;
iRectHt := HeadingRect.Bottom - HeadingRect.Top;
iIndent := (TempRect.Right - TempRect.Left) div 20;
iSpaces := (iRectHt div 20) * BORDER;
if iSpaces = 0 then iSpaces := 1;
TempRect.Top := TempRect.Top + iSpaces ;
TempRect.Bottom := TempRect.Top + g_RectHeight ;
TempRect.Left := TempRect.Left + iIndent + BUTTON_WIDTH + 1;
TempRect.Right := TempRect.Right - (iIndent + BUTTON_WIDTH + 1);
Brush.Color := clBtnFace;
Brush.Style := bsSolid;
FillRect( TempRect );
DrawText( Handle, pMonth, Length( sMonth ), TempRect,
( DT_CENTER or DT_TOP or DT_SINGLELINE ) );
end;
StrDispose( pMonth );
end;
procedure TCalenPnl.DrawDaysHeader;
var
i, iDayWidth: Integer;
pDay: PChar;
ARect: TRect;
begin
Case DayWidth of
dw1Char : iDayWidth := 1;
dw2Char : iDayWidth := 2;
dw3Char : iDayWidth := 3;
else iDayWidth := 1;
end;
pDay := StrAlloc( 3 );
ARect := HeadingRect;
ARect.Right := ARect.Left + g_Width;
if ShowDate then ARect.Top := ARect.Top + g_RectHeight ;
{ Cycle through the days }
Canvas.Font.Style := [fsBold]; {make Days Bold}
for i := 0 to 6 do
begin
if (i = 0) or (i = 6) then Canvas.Font.Color := clRed
else Canvas.Font.Color := clBlack;
StrPCopy( pDay, Copy(g_DayTitles[i], 1, iDayWidth));
DrawText( Canvas.Handle, pDay, iDayWidth, ARect,
( DT_CENTER or DT_VCENTER or DT_SINGLELINE ) );
ARect.Left := ARect.Right;
ARect.Right := ARect.Right + g_Width;
end;
Canvas.Font.Color := clBlack;
Canvas.Font.Style := []; {reset Days <> Bold}
{ Draw line below days }
with Canvas do
begin
ARect.Top := CalendarRect.Top - 4;
ARect.Left := HeadingRect.Left;
ARect.Right := HeadingRect.Right;
Pen.Color := clBtnHighlight;
MoveTo( ARect.Left , ARect.Top);
LineTo( ARect.Right, ARect.Top );
Pen.Color := clBtnShadow;
MoveTo( ARect.Left, ARect.Top + 1 );
LineTo( ARect.Right, ARect.Top + 1 );
end;
StrDispose( pDay );
end;
procedure TCalenPnl.DrawDates;
var
nIndex, nWeek, nDay: Integer;
pDate: PChar;
TempRect: Trect;
begin
pDate := StrAlloc( 3 );
With Canvas do
begin
{ Define normal font }
Font.Style := [];
Pen.Color := clBlack;
{ Cycle through the weeks }
for nWeek := 1 to 6 do
begin
{ Cycle through the days }
for nDay := 1 to 7 Do
begin
nIndex := nDay + ( ( nWeek - 1 ) * 7 );
StrPCopy( pDate, g_DateArray[nIndex] );
TempRect := CalendarRect; {OPTIMIZE: can it go outside loop?}
With TempRect Do
begin
Left := Left + (g_Width * (nDay - 1));
Top := Top + (g_RectHeight * (nWeek -1));
Bottom := Top + g_RectHeight ;
Right := Left + g_Width;
end;
if (nDay = 1) or (nDay = 7) then Font.Color := clRed else Font.Color := clBlack;
DrawText( Handle, pDate, Length( g_DateArray[nIndex] ),
TempRect, ( DT_CENTER or DT_VCENTER or DT_TOP or DT_SINGLELINE ) );
Font.Color := clBlack;
end;
end;
end;
StrDispose( pDate );
end;
procedure TCalenPnl.LoadDateArray;
var
nIndex : Integer;
nBeginIndex, nEndIndex : Integer;
begin
nBeginIndex := GetMonthBegin;
nEndIndex := nBeginIndex + DaysInMonth(FMonth, FYear) - 1;
for nIndex := 1 to 42 do
begin
If ( nIndex < nBeginIndex ) or ( nIndex > nEndIndex ) Then
g_DateArray[nIndex] := ' '
else
g_DateArray[nIndex] := IntToStr( ( nIndex - nBeginIndex ) + 1 );
end;
end;
function TCalenPnl.GetMonthBegin: Integer;
var
FirstDate: TDateTime;
begin
FirstDate := EncodeDate( FYear, FMonth, 1 );
Result := DayOfWeek( FirstDate ); { day of week for 1st of month }
end;
function TCalenPnl.DaysInMonth(nMonth, nYear : Integer): Integer;
begin
Result := DAYS_IN_MONTH[nMonth]; { leap-year Feb is special }
if ( nMonth = 2 ) and IsLeapYear(nYear) then Inc( Result );
end;
function TCalenPnl.IsLeapYear(AYear: Integer): Boolean;
begin
Result := (AYear mod 4 = 0) and ((AYear mod 100 <> 0) or (AYear mod 400 = 0));
end;
function TCalenPnl.SetDate(nDays : Integer): Boolean;
var
aY, aM, aD: Word;
PrevDay: Word;
begin
Result := True;
try
{Save current date information}
g_PrevDateIndex := g_CurrDateIndex;
DecodeDate(FCalendarDate, g_PrevYear, g_PrevMonth, PrevDay);
{Change the date and update member variables}
FCalendarDate := FCalendarDate + nDays;
DecodeDate(FCalendarDate, aY, aM, aD);
g_CurrDateIndex := ( aD + GetMonthBegin ) - 1;
{Reload Date Array & paint ONLY if month or year changed}
If (aM <> g_PrevMonth) or (aY <> g_PrevYear)Then
begin
FMonth := aM;
FYear := aY;
LoadDateArray;
end;
FDay := aD;
except
MessageBeep(MB_ICONEXCLAMATION);
Result := False;
end;
end;
Function TCalenPnl.ValidDate(aDate: TDateType) : Boolean;
Begin {is cool as no exception is generated by invalid date}
ValidDate := True;
With aDate do
Begin
If (aMonth > 12) Or (aMonth < 1) Or (aDay < 1) or (aYear < 1) or (aYear > 9999) then
Begin
ValidDate := False;
Exit;
End;
If (aMonth = 2) And IsLeapYear(Integer(aYear)) then Dec(aDay);
If aDay > DaysInMonth(aMonth, aYear) then ValidDate := False;
End;
End;
procedure TCalenPnl.SetCalendarDate(aDate: TDateTime);
var
aYear, aMonth, aDay: Word;
begin
try
if FCalendarDate <> aDate then
begin
DecodeDate(aDate, aYear, aMonth, aDay);
FCalendarDate := aDate;
FYear := Integer(aYear);
FMonth := Integer(aMonth);
FDay := Integer(aDay);
LoadDateArray;
DateChange;
Refresh;
end;
except
MessageBeep(MB_ICONEXCLAMATION);
end;
end;
procedure TCalenPnl.SetMonth(Value: Integer);
var
mDate : TDateType;
wValue, aY, aM, aD: Word;
iDaysInM : word;
begin {no test for new <> old as that would fail at startup}
if (Value < 1) or (Value > 12) then
begin {first test}
MessageBeep(MB_ICONEXCLAMATION);
Exit;
end;
wValue := Word(Value);
iDaysInM := DaysInMonth(wValue, FYear);
if iDaysInM < FDay then FDay := iDaysInM;
with mDate do
begin
aMonth := wValue; aDay := Word(FDay); aYear := Word(FYear);
end;
if ValidDate(mDate) then {2nd test}
begin
FCalendarDate := EncodeDate(Word(FYear), wValue, Word(FDay));
DecodeDate( FCalendarDate, aY, aM, aD);
g_CurrDateIndex := ( aD + GetMonthBegin ) - 1;
FYear := Integer(aY);
FMonth := Integer(aM);
FDay := Integer(aD);
DateChange;
LoadDateArray;
Refresh;
end
else MessageBeep(MB_ICONEXCLAMATION);
end;
procedure TCalenPnl.SetDay(Value: Integer);
var
dDate : TDateType;
wValue, aY, aM, aD: Word;
begin
if (Value < 1) or (Value > DaysInMonth(FMonth, FYear)) then
begin {first test}
MessageBeep(MB_ICONEXCLAMATION);
Exit;
end;
wValue := Word(Value);
with dDate do
begin
aMonth := Word(FMonth); aDay := wValue; aYear := Word(FYear);
end;
if ValidDate(dDate) then {2nd test}
begin
FCalendarDate := EncodeDate(Word(FYear), Word(FMonth), Value);
DecodeDate( FCalendarDate, aY, aM, aD);
g_CurrDateIndex := ( FDay + GetMonthBegin ) - 1;
FYear := Integer(aY);
FMonth := Integer(aM);
FDay := Integer(aD);
DateChange;
LoadDateArray;
Refresh;
end
else MessageBeep(MB_ICONEXCLAMATION);
end;
procedure TCalenPnl.SetYear(Value: Integer);
var
yDate : TDateType;
iDaysInM, wValue, aY, aM, aD: Word;
begin
if (Value < 1) or (Value > 9999) then
begin {first test}
MessageBeep(MB_ICONEXCLAMATION);
Exit;
end;
wValue := Word(Value);
iDaysInM := DaysInMonth(FMonth, wValue);
if iDaysInM < FDay then FDay := iDaysInM;
with yDate do
begin
aMonth := Word(FMonth); aDay := Word(FDay); aYear := wValue;
end;
if ValidDate(yDate) then {2nd test}
begin
FCalendarDate := EncodeDate(wValue, Word(FMonth), Word(FDay));
DecodeDate(FCalendarDate, aY, aM, aD);
g_CurrDateIndex := ( FDay + GetMonthBegin ) - 1;
FYear := Integer(aY);
FMonth := Integer(aM);
FDay := Integer(aD);
DateChange;
LoadDateArray;
Refresh;
end
else MessageBeep(MB_ICONEXCLAMATION);
end;
procedure TCalenPnl.DrawFocusFrame( nIndex: Integer);
var
pDate :PChar;
TempRect : TRect;
begin
pDate := StrAlloc( 3 );
If ( nIndex > 0 ) and ( nIndex < 42 ) then
//following line works, but may affect DblClick
//if nIndex = g_PrevDateIndex then exit;
If g_DateArray[nIndex] <> ' ' then
begin
{ Erase Previous Date Focus }
If g_PrevDateIndex > 0 Then
begin
case g_PrevDateIndex of
1, 7, 8, 14, 15, 21, 22, 28, 29, 35, 36, 42:
Canvas.Font.Color := clRed else Canvas.Font.Color := clBlack;
end;
Canvas.Font.Style := [];
StrPCopy( pDate, g_DateArray[g_PrevDateIndex] );
Canvas.Brush.Color := clBtnFace;
TempRect := GetRectFromIndex(g_PrevDateIndex);
Canvas.FillRect(TempRect);
DrawText( Canvas.Handle, pDate, Length( g_DateArray[g_PrevDateIndex] ),
TempRect, ( DT_CENTER or DT_VCENTER or DT_TOP or DT_SINGLELINE ) );
end;
{Draw the Date in Bold font}
case nIndex of
1, 7, 8, 14, 15, 21, 22, 28, 29, 35, 36, 42: Canvas.Font.Color := clRed
else Canvas.Font.Color := clBlack;
end;
Canvas.Font.Style := [fsBold];
TempRect := GetRectFromIndex(nIndex);
StrPCopy( pDate, g_DateArray[nIndex] );
DrawText( Canvas.Handle, pDate, Length( g_DateArray[nIndex] ),
TempRect, ( DT_CENTER or DT_VCENTER or DT_TOP or DT_SINGLELINE ) );
{ Frame date with Shadow }
Canvas.Pen.Color := clBtnShadow; {clGray}
Canvas.MoveTo( TempRect.Left, TempRect.Bottom - 1 );
Canvas.LineTo( TempRect.Left, TempRect.Top );
Canvas.LineTo( TempRect.Right - 1, TempRect.Top );
{ Frame date with Highlight }
Canvas.Pen.Color := clBtnHighlight; {clWhite}
Canvas.LineTo( TempRect.Right - 1, TempRect.Bottom - 1 );
Canvas.LineTo( TempRect.Left, TempRect.Bottom - 1 );
{ Restore Canvas settings}
Canvas.Pen.Color := clBlack;
Canvas.Font.Style := [];
end;
StrDispose( pDate );
end;
function TCalenPnl.GetRectFromIndex(nIndex : Integer): TRect; {1}
var
TempRect: TRect;
nWeek : Integer;
nDay : Integer;
begin
TempRect := CalendarRect;
with TempRect do
begin
nWeek := 1; //if not initialized bloody Syntax checker returns cursor
case nIndex of //here after compile, losing ones place!
1..7 : nWeek := 1;
8..14: nWeek := 2;
15..21: nWeek := 3;
22..28: nWeek := 4;
29..35: nWeek := 5;
36..42: nWeek := 6;
end;
nDay := nIndex - ((nWeek-1) *7);
Left := Left + (g_Width * (nDay-1));
Top := Top + (g_RectHeight * (nWeek - 1) );
Bottom := Top + g_RectHeight ;
Right := Left + g_Width;
end;
Result := TempRect;
end;
function TCalenPnl.GetIndexFromDate : Integer;
begin
Result := FDay + GetMonthBegin;
end;
function TCalenPnl.GetIndexFromPoint(nLeft : Integer ; nTop : Integer) : Integer;
var
nIndex, nWeek, nDay, iHorizontal, iTopOfCal: Integer;
TempRect: Trect;
begin
TempRect := CalendarRect;
iTopOfCal := TempRect.Top;
nIndex := -1;
{Is point in the calendar rectangle?}
if ( nLeft > TempRect.Left ) and ( nTop > TempRect.Top ) and
( nLeft < TempRect.Right ) and ( nTop < TempRect.Bottom ) then
begin
iHorizontal := (( nTop - iTopOfCal ) div g_RectHeight) + 1;
if iHorizontal <= 0 then iHorizontal := 1; {if its in the CalenRect then its valid}
nWeek := iHorizontal;
TempRect.Top := TempRect.Top + ( ( nWeek - 1 ) * g_RectHeight );
TempRect.Bottom := TempRect.Top + g_RectHeight;
TempRect.Right := TempRect.Left + g_Width;
{ Determine the day number of the selected date }
for nDay := 1 to 7 do {Cycle through the days}
begin
nIndex := nDay + ( ( nWeek - 1 ) * 7 );
if ( nLeft >= TempRect.Left ) and ( nLeft <= TempRect.Right ) then
break
else
begin
TempRect.Left := TempRect.Right;
TempRect.Right := TempRect.Left + g_Width;
end;
end;
end;
Result := nIndex;
end;
procedure TCalenPnl.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
inherited MouseUp(Button, Shift, X, Y);
FButtonDown := False;
if FButton = mbRight then MouseCapture := False;
end;
procedure TCalenPnl.DateChange;
begin
if Assigned(FOnDateChange) then FOnDateChange(Self);
end;
procedure TCalenPnl.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
var
nIndex : Integer;
Key: Word;
begin
inherited MouseDown(Button, Shift, X, Y);
FButton := Button;
{Check if mouse was pressed in Left button area}
if PointInRect(GetLeftButtonRect, X, Y) then
begin
Key := Vk_Prior;
KeyDown(Key,Shift);
DateChange;
end;
{Check if mouse was pressed in Right button area}
if PointInRect(GetRightButtonRect, X, Y) then
begin
Key := Vk_Next;
KeyDown(Key,Shift);
DateChange;
end;
{Check if mouse was pressed in date area} // ouch!
if PointInRect(CalendarRect, X, Y) then
begin
g_MouseDown := True;
nIndex := GetIndexFromPoint( X, Y );
If (nIndex >= GetMonthBegin) and
(nIndex < (DaysInMonth(FMonth, FYear) + GetMonthBegin)) Then
begin
if Not SetDate(nIndex - g_CurrDateIndex) then exit;
DrawFocusFrame(nIndex);
DateChange;
end
else
g_MouseDown := False;
end;
end;
function TCalenPnl.GetLeftButtonRect: TRect;
var
TempRect: TRect;
iHt: Integer;
begin
{Define Left Button Rectangle}
iHt := (HeadingRect.Bottom - HeadingRect.Top) div 15;
TempRect.Top := HeadingRect.Top + iHt;
TempRect.Bottom := TempRect.Top + BUTTON_WIDTH;
iHt := (HeadingRect.Right - HeadingRect.Left) div 30;
TempRect.Left := HeadingRect.Left + iHt;
TempRect.Right := TempRect.Left + BUTTON_WIDTH;
Result := TempRect;
end;
function TCalenPnl.GetRightButtonRect: TRect;
var
TempRect: TRect;
iHt: Integer;
begin
{Define Right Button Rectangle}
iHt := (HeadingRect.Bottom - HeadingRect.Top) div 15;
TempRect.Top := HeadingRect.Top + iHt;
TempRect.Bottom := TempRect.Top + BUTTON_WIDTH;
iHt := (HeadingRect.Right - HeadingRect.Left) div 30;
TempRect.Left := HeadingRect.Right - (BUTTON_WIDTH + iHt);
TempRect.Right := TempRect.Left + BUTTON_WIDTH;
Result := TempRect;
end;
procedure TCalenPnl.KeyDown(var Key: Word; Shift: TShiftState);
var
iDaysIncrM, iDaysToAdd, iIncrM: integer;
begin
Case key of
VK_Left : begin {PrevDay;}
if (FMonth = 1) and (FYear = 1) and (FDay = 1) then
begin
MessageBeep(MB_ICONEXCLAMATION);
exit;
end;
if Not SetDate(-1)then exit;
If (FMonth <> g_PrevMonth) or
(FYear <> g_PrevYear) Then Refresh
else DrawFocusFrame(g_CurrDateIndex);
end;
VK_Right: begin {NextDay;}
if (FMonth = 12) and (FYear = 9999) and (FDay = 31) then
begin
MessageBeep(MB_ICONEXCLAMATION);
exit;
end;
if Not SetDate(1) then exit;
If (FMonth <> g_PrevMonth) or
(FYear <> g_PrevYear) Then Refresh
else DrawFocusFrame(g_CurrDateIndex);
end;
VK_Up : begin {PrevWeek;}
if (FMonth = 1) and (FYear = 1) and (FDay < 7) then
begin
MessageBeep(MB_ICONEXCLAMATION);
exit;
end;
if Not SetDate(-7) then exit;
If (FMonth <> g_PrevMonth) or
(FYear <> g_PrevYear) Then Refresh
else DrawFocusFrame(g_CurrDateIndex);
end;
VK_Down : begin {NextWeek;}
if (FMonth = 12) and (FYear = 9999) and (FDay > 24) then
begin
MessageBeep(MB_ICONEXCLAMATION);
exit;
end;
if Not SetDate(7) then exit;
If (FMonth <> g_PrevMonth) or
(FYear <> g_PrevYear) Then Refresh
else DrawFocusFrame(g_CurrDateIndex);
end;
VK_Prior: begin {PrevMonth;}
if (FMonth = 1) and (FYear = 1) then
begin
MessageBeep(MB_ICONEXCLAMATION);
exit;
end;
if FMonth > 1 then iIncrM := FMonth -1 else iIncrM := 12;
iDaysIncrM := DaysInMonth(iIncrM, FYear);
if (iDaysIncrM < FDay) then
iDaysToAdd := DaysInMonth(FMonth, FYear)
else iDaysToAdd := iDaysIncrM;
try
if Not SetDate(-iDaysToAdd) then exit;
Refresh;
except
MessageBeep(MB_ICONEXCLAMATION);
end;
end;
Vk_Next : begin {NextMonth;}
if (FMonth = 12) and (FYear = 9999) then
begin
MessageBeep(MB_ICONEXCLAMATION);
exit;
end;
if FMonth = 12 then iIncrM := 1 else iIncrM := FMonth + 1;
iDaysIncrM := DaysInMonth(iIncrM, FYear);
if (iDaysIncrM < FDay) then iDaysToAdd := iDaysIncrM
else iDaysToAdd := DaysInMonth(FMonth, FYear);
try
if Not SetDate(iDaysToAdd) then exit;
Refresh;
except
MessageBeep(MB_ICONEXCLAMATION);
end;
end;
VK_Home : begin {NextYear;}
{If the current year is a leap year and the date is before February 29, add 1 day}
if FYear = 9999 then
begin
MessageBeep(MB_ICONEXCLAMATION);
exit;
end;
If IsLeapYear(FYear) and
(FMonth < 3) Then if Not SetDate(1) then exit;
if Not SetDate(365) then exit;
{If the current year is a leap year and the date is after February 29, add 1 day}
If IsLeapYear(FYear) and
(FMonth > 3) Then if Not SetDate(1) then exit;
Refresh;
end;
VK_End : begin {PrevYear;}
if FYear = 1 then
begin
MessageBeep(MB_ICONEXCLAMATION);
exit;
end;
{If the current year is a leap year and the date is after February 29, subtract 1 day}
If IsLeapYear(FYear) and
(FMonth > 3) Then if Not SetDate(-1) then exit;
if Not SetDate(-365) then exit;
{If the Previous year is a leap year and the date is before February 29, subtract 1 day}
If IsLeapYear(FYear) and
(FMonth < 3) Then if Not SetDate(-1) then exit;
Refresh;
end;
VK_Return: begin
{TDateEdit( ctlParent ).Date := m_CurrentDateSelected; }
{maybe you have a use for the Return or Esc keys}
end;
{VK_Escape : FormCancel;}
else
end;
end;
procedure TCalenPnl.DrawButtons;
var
LBtnRect: TRect;
RBtnRect : TRect;
OldStyle : TBrushStyle;
begin
with Canvas do
begin
LBtnRect := GetLeftButtonRect;
RBtnRect := GetRightButtonRect;
{ Select Black Pen}
Pen.Style := psSolid;
Pen.Width := 1;
Pen.Color := clBtnShadow; {clBlack}
{ Draw Button Outlines }
Rectangle(LBtnRect.Left, LBtnRect.Top, LBtnRect.Right, LBtnRect.Bottom);
Rectangle(RBtnRect.Left, RBtnRect.Top, RBtnRect.Right, RBtnRect.Bottom);
{ Create Embossed effect - Outline left & upper in white}
Pen.Color := clBtnHighlight;
MoveTo( LBtnRect.Left + 1, LBtnRect.Bottom - 2 );
LineTo( LBtnRect.Left + 1, LBtnRect.Top + 1 );
LineTo( LBtnRect.Right - 2, LBtnRect.Top + 1 );
MoveTo( RBtnRect.Left + 1, RBtnRect.Bottom - 2 );
LineTo( RBtnRect.Left + 1, RBtnRect.Top + 1 );
LineTo( RBtnRect.Right - 2, RBtnRect.Top + 1 );
{ Create Embossed effect - Outline right & bottom in shadow }
Pen.Color := clBtnShadow; {clGray}
MoveTo( LBtnRect.Right -2, LBtnRect.Top + 1 );
LineTo( LBtnRect.Right - 2, LBtnRect.Bottom - 2 );
LineTo( LBtnRect.Left + 1, LBtnRect.Bottom - 2 );
MoveTo( RBtnRect.Right - 2, RBtnRect.Top + 1 );
LineTo( RBtnRect.Right - 2, RBtnRect.Bottom - 2 );
LineTo( RBtnRect.Left + 1, RBtnRect.Bottom - 2 );
{Draw Arrow}
Brush.Color := clBtnShadow; {clBlack clBtnShadow}
OldStyle :=Brush.Style;
Brush.Style := bsSolid;
Polygon([Point(LBtnRect.Right - 5,LBtnRect.Top + 3),
Point(LBtnRect.Right - 5,LBtnRect.Bottom - 4),
Point(LBtnRect.Left + 3,LBtnRect.Top + 7)]);
Polygon([Point(RBtnRect.Left + 4,RBtnRect.Top + 3),
Point(RBtnRect.Left + 4,RBtnRect.Bottom - 4),
Point(RBtnRect.Right - 4,RBtnRect.Top + 7)]);
{my turn - white line on arrows}
Pen.Color := clBtnHighlight;
MoveTo( LBtnRect.Left + 3, LBtnRect.Top + 8 );
LineTo( LBtnRect.Right - 5, LBtnRect.Bottom - 3);
LineTo( LBtnRect.Right - 5, LBtnRect.Top + 2 );
MoveTo( RBtnRect.Left + 4, RBtnRect.Bottom - 4 );
LineTo( RBtnRect.Right - 2, RBtnRect.Top + 7 );
Brush.Color :=clBtnFace;
Brush.Style := OldStyle;
Pen.Color := clBlack;
end;
end;
function TCalenPnl.JulDate1stWeek(JD : TDateTime) : TDateTime;
{-Return the Date of the first day in the week of Julian Year}
var
aYear, aMonth, aDay : Word;
n : integer;
JDate : TDateTime;
begin
DecodeDate(JD, aYear, aMonth, aDay);
JDate := EncodeDate(aYear, 1, 1);
if DayOfWeek(JDate) in [6, 7, 1] then n := 1 else n := -1;
while DayOfWeek(JDate) <> 2 do JDate := JDate+n;
if JD >= JDate then
Result := JDate
else
Result := JulDate1stWeek(JD-7);
end;
function TCalenPnl.WeekNo(JDate : TDateTime) : Integer;
var
W : TDatetime;
begin
W := JulDate1stWeek(JDate+31);
if JDate < W then W := JulDate1stWeek(JDate);
Result := trunc(7+JDate-W) div 7;
end;
function TCalenPnl.GetWeekNumber: Integer;
begin
Result := WeekNo(EncodeDate(FYear, FMonth, FDay));
end;
function TCalenPnl.DOY(y, m, d : Word) : Integer;
var
yy, mm, dd, Tmp1 : LongInt;
begin
yy := y;
mm := m;
dd := d;
Tmp1 := (mm + 10) div 13;
DOY := 3055 * (mm + 2) div 100 - Tmp1 * 2 - 91 +
(1 - (yy - yy div 4 * 4 + 3) div 4 +
(yy - yy div 100 * 100 + 99) div 100 -
(yy - yy div 400 * 400 + 399) div 400) * Tmp1 + dd
end; { DayOfYear }
function TCalenPnl.GetDayOfYear: Integer;
begin
result := DOY(FYear, FMonth, FDay);
end;
function TCalenPnl.GetDaysInYear: integer;
begin
If IsLeapYear(FYear) then Result := 366 else result := 365;
end;
end.