{----------------------------------------------------------------------------- The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/MPL-1.1.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is: JvQMonthCalendar.pas, released on 2003-06-28 The Initial Developer of the Original Code is André Snepvangers [asn@xs4all.nl] Copyright (C) 2003 André Snepvangers. All Rights Reserved. Contributor(s): Known Issues: TODO : show today circle -----------------------------------------------------------------------------} // $Id: QMonthCalendar.pas 10610 2006-05-19 13:35:08Z elahn $ unit QMonthCalendar; interface uses Classes, SysUtils, QControls, QButtons, QStdCtrls, QExtCtrls, QGraphics, QTypes, QForms, Qt, Types, DateUtils; resourcestring rsNow = 'Today'; type TDayOfWeekName = (Mon, Tue, Wed, Thu, Fri, Sat, Sun); TDaysOfWeek = set of TDayOfWeekName; TCustomMonthCalendar = class(TWidgetControl) private { Private declarations } FCaption : TCaption ; Bevel1: TBevel; DayLbl: array[1..42] of TLabel ; WeekDayLbl : array[1..7] of TLabel; TodayLbl: TLabel; TitlePanel: TPanel; PrevMonthBtn: TBitBtn; NextMonthBtn: TBitBtn; WeekLbl: array[1..7] of TLabel; WeekBevel: TBevel; FToday : TDateTime ; FDate : TDateTime ; FEndDate : TDateTime ; FMinDate : TDateTime ; FMaxDate : TDateTime ; FWeekNumbers : boolean ; FWidth : integer ; FHeight : integer ; FShowToday : boolean ; FShowTodayCircle : boolean ; FOnDblClick: TNotifyEvent; FOnClick: TNotifyEvent; FTextColor: TColor ; WeekWidth : integer ; FirstDay, Lastday : integer ; FWeekends: TDaysOfWeek; FWeekendColor: TColor; FSelectBmp: TBitmap; FTodayBmp: TBitmap; FSelectTodayBmp: TBitmap; procedure PaintBitmap(Bmp: TBitmap; ASize: TSize; TodayCircle: boolean; Selected: boolean); procedure ReAlign ; procedure UpdateMonth ; procedure PaintDays ; procedure SetDate( aDate : TDateTime ) ; procedure SetEndDate( aDate : TDateTime ) ; procedure SetMaxDate( aDate : TDateTime ) ; procedure SetMinDate( aDate : TDateTime ) ; procedure SetShowToday( aShow : boolean) ; procedure SetShowTodayCircle( aShow : boolean) ; procedure SetWeekNumbers( aShow : boolean) ; procedure SetMonthBackColor( aColor : TColor ); procedure SetTextColor( aColor : TColor ); procedure SetTitleTextColor( aColor : TColor ); procedure SetTitleBackColor( aColor : TColor ); procedure SetTrailingTextColor( aColor : TColor ); procedure SetFontColor(ALabel: TLabel); function GetMonthBackColor: TColor ; function GetTitleTextColor: TColor; function GetTitleBackColor: TColor ; function GetTrailingTextColor : TColor ; procedure PrevMonthBtnClick(Sender: TObject); procedure NextMonthBtnClick(Sender: TObject); procedure BtnClick(Sender: TObject); procedure Label1Click(Sender: TObject); procedure Label1DblClick(Sender: TObject); procedure SetDefaultLblProps( aLbl : TLabel ) ; procedure SetWeekendColor(Value: TColor); procedure SetWeekends(Value: TDaysOfWeek); protected procedure FontChanged ; override ; procedure KeyDown(var Key: Word; Shift: TShiftState); override; procedure Loaded; override; procedure SetParent(const aParent: TWidgetControl); override; property Weekends: TDaysOfWeek read FWeekends write SetWeekends default [Sun]; property WeekendColor: TColor read FWeekendColor write SetWeekendColor default clRed; public { Public declarations } constructor Create(AOwner: TComponent); override; destructor Destroy ; override; procedure SetBounds(ALeft, ATop , AWidth , AHeight : integer); override ; published property DateTime : TDateTime read FDate write SetDate ; // selected day property EndDate : TDateTime read FEndDate write SetEndDate; property MaxDate : TDateTime read FMaxDate write SetMaxDate; property MinDate : TDateTime read FMinDate write SetMinDate; property ShowToday : boolean read FShowToday write SetShowToday default true; property ShowTodayCircle : boolean read FShowTodayCircle write SetShowTodayCircle default true; property WeekNumbers : boolean read FWeekNumbers write SetWeekNumbers default false; property MonthBackColor: TColor read GetMonthBackColor write SetMonthBackColor default clWhite ; property TextColor: TColor read FTextColor write SetTextColor default clNormalText ; property TitleBackColor: TColor read GetTitleBackColor write SetTitleBackColor default clHighlight ; property TitleTextColor: TColor read GetTitleTextColor write SetTitleTextColor default clBrightText ; property TrailingTextColor: TColor read GetTrailingTextColor write SetTrailingTextColor default clHighlight; Property OnDblClick: TNotifyEvent read FOnDblClick write FOnDblClick; Property OnClick : TNotifyEvent read FOnClick write FOnClick; property Top; property Left; Property Font; property Hint; property Tag; property ShowHint ; property Enabled; property Visible ; Property OnEnter ; Property OnExit; property OnKeyDown; property OnKeyPress; property OnKeyUp; Property OnContextPopup; Property OnDragDrop; Property OnDragOver; property OnMouseEnter; property OnMouseDown; property OnMouseUp; property OnMouseMove; property OnMouseLeave ; property TabStop; property TabOrder; end; type TMonthCalendar = class(TCustomMonthCalendar) public { Public declarations } published property DateTime; property EndDate ; property MaxDate ; property MinDate ; property ShowToday ; property ShowTodayCircle ; property WeekNumbers; property MonthBackColor; property TextColor ; property TitleBackColor; property TitleTextColor; property TrailingTextColor; Property OnDblClick; Property OnClick; property Top; // property TabStop ; property Left; Property Font; property Hint; property Tag; property ShowHint ; property Enabled; property Visible ; Property OnEnter ; Property OnExit; property OnKeyDown; property OnKeyPress; property OnKeyUp; Property OnContextPopup; Property OnDragDrop; Property OnDragOver; property OnMouseEnter; property OnMouseDown; property OnMouseUp; property OnMouseMove; property OnMouseLeave ; property TabStop; property TabOrder; end; implementation uses Math; {$R *.dcr} procedure TCustomMonthCalendar.SetDefaultLblProps( aLbl : TLabel ) ; begin with aLbl do begin Parent := self; // ParentFont := true ; Alignment := taCenter ; Anchors := [akRight] ; Alignment := taCenter ; Autosize := false; end; end; destructor TCustomMonthCalendar.Destroy; begin FSelectBmp.Free; FTodayBmp.Free; FSelectTodayBmp.Free; inherited ; end; constructor TCustomMonthCalendar.Create(AOwner : TComponent); var i : integer ; begin inherited Create(AOwner) ; ControlStyle := ControlStyle -[csNoFocus, csSetCaption]; Height := 168 ; Width := 250 ; Color := clBase ; FSelectBmp := TBitmap.Create; FTodayBmp := TBitmap.Create; FSelectTodayBmp := TBitmap.Create; FWeekEndColor := clRed; FWeekEnds := [sun]; {$IFDEF MSWINDOWS} Font.name := 'Arial'; {$ENDIF MSWINDOWS} InputKeys := InputKeys + [ikNav, ikArrows]; FCaption := ''; FShowToday := true; FWeekNumbers := true ; ToDayLbl := TLabel.Create(self); with TodayLbl do begin Parent := self ; SetDefaultLblProps( TodayLbl ); AutoSize := true ; Font.Style := [fsBold] ; // Font.Size := Font.Size ; Caption := rsNow + ': ' + FormatDateTime(ShortDateFormat, Today ); OnClick := BtnClick ; end; TitlePanel:= TPanel.Create(self); with TitlePanel do begin Parent := self ; Width := 250 ; BevelOuter := bvNone; Font.Color := clBrightText; // Font.Size := ToDayLbl.Font.Size ; Font.Style := [fsBold]; Anchors := [akLeft, akRight]; Color := clHighLight ; Height := 2 * Font.Height ; OnClick := BtnClick ; ParentShowHint := false ; end; PrevMonthBtn := TBitBtn.Create(self); with PrevMonthBtn do begin Parent := TitlePanel ; Width := 16 ; Height := 16 ; Top := (TitlePanel.Height - Height) div 2 ; Left := Top ; Caption := '' ; Bitmap.LoadFromResourceName(HInstance,'LEFTARROW'); Anchors := [akLeft]; TabStop := false ; OnClick := PrevMonthBtnClick ; ParentShowHint := true ; end; NextMonthBtn := TBitBtn.Create(self); with NextMonthBtn do begin Parent := TitlePanel ; Width := PrevMonthBtn.Width ; Height := PrevMonthBtn.Height ; Top := PrevMonthBtn.Top ; Left := TitlePanel.Width - Top -Width; Caption := '' ; Bitmap.LoadFromResourceName(HInstance,'RIGHTARROW'); Anchors := [akRight]; OnClick := NextMonthBtnClick ; TabStop := false ; ParentShowHint := true ; end; For i:= 1 to 42 do begin DayLbl[i] := TLabel.Create(self) ; SetDefaultLblProps(DayLbl[i]); DayLbl[i].OnClick := Label1Click ; DayLbl[i].OnDblClick := Label1DblClick ; ParentShowHint := true ; end; For i:=1 to 7 do begin WeekDayLbl[i] := TLabel.Create(self) ; SetDefaultLblProps( WeekDayLbl[i] ); WeekDayLbl[i].Font.Color := clActiveHighLight ; OnClick := BtnClick ; end ; For i:=1 to 6 do begin WeekLbl[i] := TLabel.Create(self); SetDefaultLblProps( WeekLbl[i] ); WeekLbl[i].Font.Color := clActiveHighLight ; OnClick := BtnClick ; end ; Bevel1:= TBevel.Create(self); Bevel1.Parent := self ; with Bevel1 do begin Left := 28 ; Top := 46 ; Width := 214 ; Height := 2 ; Anchors := [ akRight]; ParentShowHint := False ; Shape := bsTopLine ; ShowHint := False ; end; WeekBevel := TBevel.Create(self); WeekBevel.Parent := self ; with WeekBevel do begin Left := 24 ; Top := 52 ; Width := 2 ; Height := 94 ; Anchors := [akTop, akRight] ; Shape := bsLeftLine ; end ; FWeekNumbers := false ; WeekWidth := WeekBevel.Left + 2 ; FToday := Today ; FDate := FToday ; FShowTodayCircle := true; ToDayLbl.Caption := rsNow + ': ' + FormatDateTime(ShortDateFormat, Today ); PaintDays; ReAlign; UpdateMonth ; end; procedure TCustomMonthCalendar.Loaded; begin inherited Loaded; // ReAlign; end; procedure TCustomMonthCalendar.SetParent(const aParent: TWidgetControl); begin inherited; // ReAlign; end; procedure TCustomMonthCalendar.SetBounds(ALeft, ATop , AWidth , AHeight : integer); begin // if csdesigning in componentstate then begin AWidth := Fwidth; AHeight := FHeight; end; inherited; end; procedure TCustomMonthCalendar.FontChanged ; begin if not (csCreating in Controlstate) then ReAlign ; end; procedure TCustomMonthCalendar.PaintBitmap(Bmp: TBitmap; ASize: TSize; TodayCircle: boolean; Selected: boolean); begin with Bmp do begin Width := ASize.cx ; Height := ASize.cy ; with Bmp.Canvas do begin Brush.Color := GetMonthBackColor; Brush.Style := bsSolid; FillRect(Bounds(0, 0, ASize.cx, ASize.cy)); Brush.Color := GetTitleBackColor; if Selected then Brush.Style := bsSolid else Brush.Style := bsClear; Pen.Color := clRed; Pen.Width := 2; if TodayCircle then Pen.Style := psSolid else Pen.Style := psClear; Ellipse(0, 0, ASize.cx, ASize.cy); end; end; end; procedure TCustomMonthCalendar.ReAlign ; // if font changes var i, j: integer ; oColor: TColor; Canvas: TControlCanvas; ASize: TSize; begin if csDestroying in ComponentState then exit; Canvas := TControlCanvas.Create; Canvas.Control := self; // oColor := DayLbl[1].Font.Color; // DayLbl[1].Font := Font; // DayLbl[1].Font.Color := oColor; Canvas.Font.Assign(Font); // with DayLbl[1] do begin ASize := Canvas.TextExtent('9999'); end; Canvas.free; PaintBitmap(FSelectBmp, ASize, false, true); PaintBitmap(FTodayBmp, ASize, true, false); PaintBitmap(FSelectTodayBmp, ASize, true, true); FWidth := 8 * ASize.cx + 4; inherited Width := FWidth ; TitlePanel.Width := FWidth ; TitlePanel.Font.name := Font.name ; TitlePanel.Font.size := Font.Size ; with TitlePanel do begin Top := 0 ; Left := 0 ; Height := 2 * ASize.cy; end; PrevMonthBtn.Top:=1 + (TitlePanel.Height - PrevMonthBtn.Height) div 2 ; NextMonthBtn.Top:= PrevMonthBtn.Top ; FHeight := 7 * ASize.cy + TitlePanel.Height + 6 + ToDayLbl.Height ; inherited Height := FHeight ; DayLbl[1].Top := TitlePanel.Height + 6 ; with WeekBevel do begin Left := ASize.cx ; Top := TitlePanel.Height + ASize.cy + 6 ; Height := 6 * ASize.cy ; end; with Bevel1 do begin Left := DayLbl[1].Width + 4 ; Top := TitlePanel.Height + ASize.cy + 2 ; Width := 13 * ASize.cx ; end; oColor := WeekDayLbl[1].Font.Color ; for i:= 1 to 7 do begin WeekDayLbl[i].Font := Font ; with WeekDayLbl[i] do begin Font.Color := oColor ; Width := ASize.cx ; Height := ASize.cy ; Left := WeekBevel.Left + 4 + (i - 1) * Width; Top := TitlePanel.Height + 2 ; end; end; for j:= 1 to 6 do begin oColor := WeekLbl[j].Font.Color; WeekLbl[j].Font := Font ; with WeekLbl[j] do begin Font.Color := oColor ; Width := ASize.cx ; Height := ASize.cy ; Left := 0 ; Top := WeekDayLbl[1].Top + j * Height + 4 ; end; for i:= 1 to 7 do begin with DayLbl[ i + (j-1)* 7] do begin oColor := Font.Color; Font := DayLbl[1].Font; Font.Color := oColor; Width := ASize.cx ; Height := ASize.cy ; Top := WeekLbl[j].Top ; Left := WeekBevel.Left + 4 + (i-1) * Width end; end; end; if ShowToday then begin TodayLbl.Font.Name := Font.Name ; TodayLbl.Font.Height := Font.Height ; // TodayLbl.Anchors := [akLeft] ; if FShowTodayCircle then TodayLbl.Left := ASize.cx + 4 else TodayLbl.Left := 2 * ASize.cx + 4 ; TodayLbl.Top := Height - TodayLbl.Height - 1; end else if not FWeekNumbers then begin FWidth := FWidth - WeekBevel.Left - 1 ; inherited Width := FWidth ; end ; end; procedure TCustomMonthCalendar.PaintDays ; // weekdays var aDate : TDateTime ; i : integer ; begin aDate := IncDay( FDate , 1 - DayOftheWeek( FDate ) ); for i:=1 to 7 do begin with WeekDayLbl[i] do begin Caption := FormatDateTime( 'ddd' ,aDate ); end; aDate := IncDay( aDate ) ; end; end; procedure TCustomMonthCalendar.UpdateMonth; // days of month var i,j : integer ; Days : integer ; begin TitlePanel.Caption := FormatDateTime('mmmm yyyy', FDate ); FirstDay := DayOfTheWeek(StartOfTheMonth(FDate)); LastDay := DayOfTheMonth( IncDay( StartOfTheMonth( FDate ), -1 ) ) ; Days := DaysInMonth( FDate ); for i := pred(FirstDay) downto 1 do begin with DayLbl[i] do begin Caption := inttostr(LastDay); Font.Color := TextColor ; Color := MonthBackColor ; Enabled := false ; Bitmap := nil; dec(LastDay); end; end; j:= WeekOfTheYear( StartOfTheMonth(FDate) ) ; for i := 1 to 6 do begin with WeekLbl[i] do begin Caption := inttostr(j); end; inc(j) ; end; for i:= 1 to Days do begin with DayLbl[FirstDay + i -1 ] do begin Enabled := true ; Tag := Floor(StartOfTheMonth(FDate))+ i - 1; SetFontColor(DayLbl[FirstDay + i -1]) ; if i = DayOfTheMonth(FDate) then begin Font.Color := TitleTextColor; if FShowTodayCircle and (Tag = Today) then Bitmap := FSelectTodayBmp else Bitmap := FSelectBmp; invalidate; end else begin if FShowTodayCircle and (Tag = Today) then Bitmap := FTodayBmp else Bitmap := nil; end; Caption := inttostr(i); end; end; j := 1 ; for i:= FirstDay+Days to 42 do begin with DayLbl[i] do begin Caption := inttostr(j); Font.Color := clText ; Enabled := false ; Color := MonthBackColor ; Bitmap := nil; end; inc(j) ; end; end; procedure TCustomMonthCalendar.PrevMonthBtnClick(Sender: TObject); begin SetFocus; FDate := IncMonth( FDate , -1 ); if Assigned( FOnClick ) then FonClick( Sender ); UpdateMonth; end; procedure TCustomMonthCalendar.BtnClick(Sender: TObject); begin SetFocus; end; procedure TCustomMonthCalendar.NextMonthBtnClick(Sender: TObject); begin SetFocus; FDate := IncMonth( FDate ); if Assigned( FOnClick ) then FonClick( Sender ); UpdateMonth ; end; procedure TCustomMonthCalendar.SetWeekNumbers( aShow : boolean) ; var i: integer; begin for i := 1 to 7 do WeekDayLbl[i].visible := aShow; WeekBevel.visible := false; if AShow <> FWeekNumbers then begin FWeekNumbers := aShow ; ReAlign; end; end; procedure TCustomMonthCalendar.SetDate( aDate : TDateTime ) ; begin FDate := aDate ; UpdateMonth ; end; procedure TCustomMonthCalendar.SetEndDate( aDate : TDateTime ) ; begin FEndDate := aDate ; end; procedure TCustomMonthCalendar.SetMaxDate( aDate : TDateTime ) ; begin FMaxDate := aDate ; end; procedure TCustomMonthCalendar.SetMinDate( aDate : TDateTime ) ; begin FMinDate := aDate ; end; procedure TCustomMonthCalendar.SetShowToday( aShow : boolean) ; begin if aShow <> FShowToday then TodayLbl.Visible := aShow ; FShowToday := aShow ; invalidate; end; procedure TCustomMonthCalendar.SetShowTodayCircle( aShow : boolean) ; begin FShowTodayCircle := aShow ; // nothing so far end; procedure TCustomMonthCalendar.SetMonthBackColor( aColor : TColor ); begin if aColor <> Color then begin Color := aColor ; end; end; procedure TCustomMonthCalendar.SetTextColor( aColor : TColor ); begin FTextColor := aColor ; UpdateMonth ; end; procedure TCustomMonthCalendar.SetTitleBackColor( aColor : TColor ); begin TitlePanel.Color := aColor ; PaintDays; end; procedure TCustomMonthCalendar.SetTitleTextColor( aColor : TColor ); begin TitlePanel.Font.Color := aColor ; PaintDays; end; procedure TCustomMonthCalendar.SetTrailingTextColor( aColor : TColor ); var i : integer ; begin for i:=1 to 7 do with WeekDayLbl[i] do Font.Color := aColor ; for i:=1 to 6 do with WeekLbl[i] as TLabel do Font.Color := aColor ; end; procedure TCustomMonthCalendar.SetWeekendColor(Value: TColor); begin if Value <> FWeekendColor then begin FWeekendColor := Value; // UpdatePopup; end; end; procedure TCustomMonthCalendar.SetWeekends(Value: TDaysOfWeek); begin if Value <> FWeekends then begin FWeekends := Value; // UpdatePopup; end; end; function TCustomMonthCalendar.GetTrailingTextColor: TColor ; begin with WeekDayLbl[1] do Result := Font.Color ; end; function TCustomMonthCalendar.GetMonthBackColor: TColor ; begin Result := Color ; end; function TCustomMonthCalendar.GetTitleTextColor: TColor; begin Result := TitlePanel.Font.Color ; end; function TCustomMonthCalendar.GetTitleBackColor: TColor ; begin Result := TitlePanel.Color ; end; procedure TCustomMonthCalendar.SetFontColor( ALabel: TLabel); const WeekDays: array [1..7] of TDayOfWeekName = (Mon, Tue, Wed, Thu, Fri, Sat, Sun); var WkDay: TDayOfWeekName; begin WkDay := WeekDays[DayOfTheWeek(ALabel.Tag)]; if WkDay in Weekends then ALabel.Font.Color := WeekendColor else ALabel.Font.Color := TextColor; end; procedure TCustomMonthCalendar.Label1Click(Sender: TObject); var curDay : integer ; begin // SetFocus ; curDay := DayOfTheMonth(FDate) ; SetFontColor(DayLbl[FirstDay + curDay - 1]); with DayLbl[FirstDay + curDay - 1] do begin if ShowTodayCircle and (Tag = Today) then Bitmap := FTodayBmp else Bitmap := nil; SetFontColor(DayLbl[FirstDay + curDay - 1]); end; with Sender as TLabel do begin FDate := IncDay( FDate , strtoint(Caption) - curDay ); Font.Color := TitleTextColor ; if ShowTodayCircle and (Tag = Floor(Now)) then Bitmap := FSelectTodayBmp else Bitmap := FSelectBmp; end; if Assigned( FOnClick ) then FonClick( Sender ); end; procedure TCustomMonthCalendar.Label1DblClick(Sender: TObject); begin if Assigned( FOnDblClick ) then FonDblClick( Sender ); end; procedure TCustomMonthCalendar.KeyDown(var Key: Word; Shift: TShiftState); begin case Key of Key_Up: DateTime := IncWeek(FDate, -1); Key_Down: DateTime := IncWeek(FDate, 1); Key_Left: DateTime := IncDay(FDate, -1); Key_Right: DateTime := IncDay(FDate, 1); Key_Home: DateTime := Now ; Key_Prior: DateTime := IncMonth(FDate, -1); Key_Next: DateTime := IncMonth(Fdate, 1); end; end; end.