home *** CD-ROM | disk | FTP | other *** search
- unit Calendar;
-
- interface
-
- uses Classes, Controls, Messages, Windows, Forms, Graphics, StdCtrls,
- Grids, SysUtils;
-
- type
- TDayOfWeek = 0..6;
-
- TCalendar = class(TCustomGrid)
- private
- FDate: TDateTime;
- FMonthOffset: Integer;
- FOnChange: TNotifyEvent;
- FReadOnly: Boolean;
- FStartOfWeek: TDayOfWeek;
- FUpdating: Boolean;
- FUseCurrentDate: Boolean;
- function GetCellText(ACol, ARow: Integer): string;
- function GetDateElement(Index: Integer): Integer;
- procedure SetCalendarDate(Value: TDateTime);
- procedure SetDateElement(Index: Integer; Value: Integer);
- procedure SetStartOfWeek(Value: TDayOfWeek);
- procedure SetUseCurrentDate(Value: Boolean);
- function StoreCalendarDate: Boolean;
- protected
- procedure Change; dynamic;
- procedure ChangeMonth(Delta: Integer);
- procedure Click; override;
- function DaysPerMonth(AYear, AMonth: Integer): Integer; virtual;
- function DaysThisMonth: Integer; virtual;
- procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override;
- function IsLeapYear(AYear: Integer): Boolean; virtual;
- function SelectCell(ACol, ARow: Longint): Boolean; override;
- procedure WMSize(var Message: TWMSize); message WM_SIZE;
- public
- constructor Create(AOwner: TComponent); override;
- property CalendarDate: TDateTime read FDate write SetCalendarDate stored StoreCalendarDate;
- property CellText[ACol, ARow: Integer]: string read GetCellText;
- procedure NextMonth;
- procedure NextYear;
- procedure PrevMonth;
- procedure PrevYear;
- procedure UpdateCalendar; virtual;
- published
- property Align;
- property Anchors;
- property BorderStyle;
- property Color;
- property Constraints;
- property Ctl3D;
- property Day: Integer index 3 read GetDateElement write SetDateElement stored False;
- property DragCursor;
- property DragKind;
- property DragMode;
- property Enabled;
- property Font;
- property GridLineWidth;
- property Month: Integer index 2 read GetDateElement write SetDateElement stored False;
- property ParentColor;
- property ParentFont;
- property ParentShowHint;
- property PopupMenu;
- property ReadOnly: Boolean read FReadOnly write FReadOnly default False;
- property ShowHint;
- property StartOfWeek: TDayOfWeek read FStartOfWeek write SetStartOfWeek;
- property TabOrder;
- property TabStop;
- property UseCurrentDate: Boolean read FUseCurrentDate write SetUseCurrentDate default True;
- property Visible;
- property Year: Integer index 1 read GetDateElement write SetDateElement stored False;
- property OnClick;
- property OnChange: TNotifyEvent read FOnChange write FOnChange;
- property OnDblClick;
- property OnDragDrop;
- property OnDragOver;
- property OnEndDock;
- property OnEndDrag;
- property OnEnter;
- property OnExit;
- property OnKeyDown;
- property OnKeyPress;
- property OnKeyUp;
- property OnStartDock;
- property OnStartDrag;
- end;
-
- implementation
-
- constructor TCalendar.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- { defaults }
- FUseCurrentDate := True;
- FixedCols := 0;
- FixedRows := 1;
- ColCount := 7;
- RowCount := 7;
- ScrollBars := ssNone;
- Options := Options - [goRangeSelect] + [goDrawFocusSelected];
- FDate := Date;
- UpdateCalendar;
- end;
-
- procedure TCalendar.Change;
- begin
- if Assigned(FOnChange) then FOnChange(Self);
- end;
-
- procedure TCalendar.Click;
- var
- TheCellText: string;
- begin
- inherited Click;
- TheCellText := CellText[Col, Row];
- if TheCellText <> '' then Day := StrToInt(TheCellText);
- end;
-
- function TCalendar.IsLeapYear(AYear: Integer): Boolean;
- begin
- Result := (AYear mod 4 = 0) and ((AYear mod 100 <> 0) or (AYear mod 400 = 0));
- end;
-
- function TCalendar.DaysPerMonth(AYear, AMonth: Integer): Integer;
- const
- DaysInMonth: array[1..12] of Integer = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
- begin
- Result := DaysInMonth[AMonth];
- if (AMonth = 2) and IsLeapYear(AYear) then Inc(Result); { leap-year Feb is special }
- end;
-
- function TCalendar.DaysThisMonth: Integer;
- begin
- Result := DaysPerMonth(Year, Month);
- end;
-
- procedure TCalendar.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
- var
- TheText: string;
- begin
- TheText := CellText[ACol, ARow];
- with ARect, Canvas do
- TextRect(ARect, Left + (Right - Left - TextWidth(TheText)) div 2,
- Top + (Bottom - Top - TextHeight(TheText)) div 2, TheText);
- end;
-
- function TCalendar.GetCellText(ACol, ARow: Integer): string;
- var
- DayNum: Integer;
- begin
- if ARow = 0 then { day names at tops of columns }
- Result := ShortDayNames[(StartOfWeek + ACol) mod 7 + 1]
- else
- begin
- DayNum := FMonthOffset + ACol + (ARow - 1) * 7;
- if (DayNum < 1) or (DayNum > DaysThisMonth) then Result := ''
- else Result := IntToStr(DayNum);
- end;
- end;
-
- function TCalendar.SelectCell(ACol, ARow: Longint): Boolean;
- begin
- if ((not FUpdating) and FReadOnly) or (CellText[ACol, ARow] = '') then
- Result := False
- else Result := inherited SelectCell(ACol, ARow);
- end;
-
- procedure TCalendar.SetCalendarDate(Value: TDateTime);
- begin
- FDate := Value;
- UpdateCalendar;
- Change;
- end;
-
- function TCalendar.StoreCalendarDate: Boolean;
- begin
- Result := not FUseCurrentDate;
- end;
-
- function TCalendar.GetDateElement(Index: Integer): Integer;
- var
- AYear, AMonth, ADay: Word;
- begin
- DecodeDate(FDate, AYear, AMonth, ADay);
- case Index of
- 1: Result := AYear;
- 2: Result := AMonth;
- 3: Result := ADay;
- else Result := -1;
- end;
- end;
-
- procedure TCalendar.SetDateElement(Index: Integer; Value: Integer);
- var
- AYear, AMonth, ADay: Word;
- begin
- if Value > 0 then
- begin
- DecodeDate(FDate, AYear, AMonth, ADay);
- case Index of
- 1: if AYear <> Value then AYear := Value else Exit;
- 2: if (Value <= 12) and (Value <> AMonth) then AMonth := Value else Exit;
- 3: if (Value <= DaysThisMonth) and (Value <> ADay) then ADay := Value else Exit;
- else Exit;
- end;
- FDate := EncodeDate(AYear, AMonth, ADay);
- FUseCurrentDate := False;
- UpdateCalendar;
- Change;
- end;
- end;
-
- procedure TCalendar.SetStartOfWeek(Value: TDayOfWeek);
- begin
- if Value <> FStartOfWeek then
- begin
- FStartOfWeek := Value;
- UpdateCalendar;
- end;
- end;
-
- procedure TCalendar.SetUseCurrentDate(Value: Boolean);
- begin
- if Value <> FUseCurrentDate then
- begin
- FUseCurrentDate := Value;
- if Value then
- begin
- FDate := Date; { use the current date, then }
- UpdateCalendar;
- end;
- end;
- end;
-
- { Given a value of 1 or -1, moves to Next or Prev month accordingly }
- procedure TCalendar.ChangeMonth(Delta: Integer);
- var
- AYear, AMonth, ADay: Word;
- NewDate: TDateTime;
- CurDay: Integer;
- begin
- DecodeDate(FDate, AYear, AMonth, ADay);
- CurDay := ADay;
- if Delta > 0 then ADay := DaysPerMonth(AYear, AMonth)
- else ADay := 1;
- NewDate := EncodeDate(AYear, AMonth, ADay);
- NewDate := NewDate + Delta;
- DecodeDate(NewDate, AYear, AMonth, ADay);
- if DaysPerMonth(AYear, AMonth) > CurDay then ADay := CurDay
- else ADay := DaysPerMonth(AYear, AMonth);
- CalendarDate := EncodeDate(AYear, AMonth, ADay);
- end;
-
- procedure TCalendar.PrevMonth;
- begin
- ChangeMonth(-1);
- end;
-
- procedure TCalendar.NextMonth;
- begin
- ChangeMonth(1);
- end;
-
- procedure TCalendar.NextYear;
- begin
- if IsLeapYear(Year) and (Month = 2) and (Day = 29) then Day := 28;
- Year := Year + 1;
- end;
-
- procedure TCalendar.PrevYear;
- begin
- if IsLeapYear(Year) and (Month = 2) and (Day = 29) then Day := 28;
- Year := Year - 1;
- end;
-
- procedure TCalendar.UpdateCalendar;
- var
- AYear, AMonth, ADay: Word;
- FirstDate: TDateTime;
- begin
- FUpdating := True;
- try
- DecodeDate(FDate, AYear, AMonth, ADay);
- FirstDate := EncodeDate(AYear, AMonth, 1);
- FMonthOffset := 2 - ((DayOfWeek(FirstDate) - StartOfWeek + 7) mod 7); { day of week for 1st of month }
- if FMonthOffset = 2 then FMonthOffset := -5;
- MoveColRow((ADay - FMonthOffset) mod 7, (ADay - FMonthOffset) div 7 + 1,
- False, False);
- Invalidate;
- finally
- FUpdating := False;
- end;
- end;
-
- procedure TCalendar.WMSize(var Message: TWMSize);
- var
- GridLines: Integer;
- begin
- GridLines := 6 * GridLineWidth;
- DefaultColWidth := (Message.Width - GridLines) div 7;
- DefaultRowHeight := (Message.Height - GridLines) div 7;
- end;
-
- end.
-