home *** CD-ROM | disk | FTP | other *** search
- 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.
-
- ******NEW******
- Roland Weinschuetz has added a considerable degree of functionality by
- adding some sorely needed properties to TCalenPnl. Roland agreed that it
- would benefit from some European style date facilities, and did an excellent
- job of implementing them. The new properties are listed below.
- ******Resume******
-
- 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.
-
- ***NEW, added by Roland, and marked // RW: in the .PAS file.
-
- GermanDate: Boolean switch to enable German date.
- ColHoliday,
- ColWeekend,
- ColMarked: TColor, to mark important dates.
- Holidays,
- Markdays: TStrings, for storing holidays and special days as strings.
-
- There is some repetition 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.
-
- Roland Weinschuetz has added the dynamic StartofWeek option that the earlier
- TCalenPnl needed.
-
- 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,
- StdCtrls,
- 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;
- // RW: Changes for german date:
- // index +1; Sunday is copied to last index
- g_DayTitles : Array[0..7] 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;
- // RW: Boolean to switch to german date
- FGermanDate: Boolean;
- // RW: Special colors may be chosen
- FColHoliday: TColor;
- FColWeekend: TColor;
- FColMarked: TColor;
- // RW: Properties for storing holidays and special days as strings
- // format: dd.mm.
- FHolidays: TStrings;
- FMarkdays: TStrings;
-
- 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;
- // RW: this one sets the german date
- procedure SetGermanDate(Value: Boolean);
- // RW: adapted DayOfWeek-function to fit german date
- function rDayOfWeek(vDate: TDateTime) : Integer;
- // RW: set color properties
- procedure SetColHoliday(Value: TColor);
- procedure SetColWeekend(Value: TColor);
- procedure SetColMarked(Value: TColor);
- // RW: build string lists
- procedure SetHolidays(Value: TStrings);
- procedure SetMarkdays(Value: TStrings);
- // RW: returns TRUE if parameter denotes a special day
- function CheckHoliday(DateList: TStrings; sd: PChar; m: integer) : Boolean;
-
- 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;
- // RW: frees special date string lists
- destructor Destroy; 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 False;
- property UseLongDate: Boolean read GetUseLongDate write SetUseLongDate; {defaults to False}
- property DayWidth: TDayWidth read FDayWidth write SetDayWidth default dw2Char;
- 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;
- // RW: these will be visible in the object-inspector
- property GermanDate: Boolean read FGermanDate write SetGermanDate;
- property ColHoliday: TColor read FColHoliday write SetColHoliday;
- property ColWeekend: TColor read FColWeekend write SetColWeekend default clPurple;
- property ColMarked: TColor read FColMarked write SetColMarked;
- property Holidays: tstrings read FHolidays write SetHolidays;
- property Markdays: tstrings read FMarkdays write SetMarkdays;
-
- 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);
- // RW: Create the stringlists for special days
- FHolidays := TStringList.Create;
- FMarkdays := TStringList.Create;
- Height := 160;
- Width := 160;
- BevelOuter := bvRaised;
- BevelInner := bvLowered;
- BevelWidth := 1;
- BorderStyle := bsNone;
- BorderWidth := 1;
- DayWidth := dw2Char;
- for iCount := 0 to 6 do g_DayTitles[iCount] := ShortDayNames[iCount +1];
- // RW: copy sunday to index 7 for german date
- g_DayTitles[7] := ShortDayNames[1];
- FCalendarDate := Date;
- FShowDate := False;
- DecodeDate(FCalendarDate, aY, aM, aD );
- FMonth := Integer(aM);
- FDay := Integer(aD);
- FYear := Integer(aY);
- g_PrevDateIndex := 0;
- LoadDateArray;
- SetDate(0);
- g_MouseDown := False;
- end;
-
- // RW: free special date stringlist
- destructor TCalenPnl.Destroy;
- begin
- FHolidays.Free;
- FMarkdays.Free;
- inherited Destroy;
- 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
- // RW: german date: (i=5) or (i=6)
- if GermanDate = False then
- begin
- if (i = 0) or (i = 6) then Canvas.Font.Color := ColWeekend
- else Canvas.Font.Color := clBlack;
- StrPCopy( pDay, Copy(g_DayTitles[i], 1, iDayWidth));
- end
- else
- begin
- if (i = 5) or (i = 6) then Canvas.Font.Color := ColWeekend
- else Canvas.Font.Color := clBlack;
- StrPCopy( pDay, Copy(g_DayTitles[i+1], 1, iDayWidth));
- end;
-
- 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 GermanDate = False then
- if (nDay = 1) or (nDay = 7) then
- Font.Color := ColWeekend else Font.Color := clBlack
- else
- if (nDay = 6) or (nDay = 7) then
- Font.Color := ColWeekend else Font.Color := clBlack;
-
- if CheckHoliday(Holidays, pDate, FMonth) then
- Font.Color := ColHoliday;
- if CheckHoliday(Markdays, pDate, FMonth) then
- Canvas.Font.Color := ColMarked;
-
- 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 );
- // RW: took me long time to find it: central point to adapt date-format
- Result := rDayOfWeek( FirstDate )
- 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);
- type
- ByteSet = set of Byte;
- var
- pDate :PChar;
- TempRect : TRect;
- setWE : ByteSet;
- begin
- pDate := StrAlloc( 3 );
- setWE := [];
-
- // RW: this set is used throughout the rest of the function
- // RW: so german date has to be checked but once
- if GermanDate = False then
- setWE := setWE + [1, 7, 8, 14, 15, 21, 22, 28, 29, 35, 36, 42]
- else
- setWE := setWE + [6, 7, 13, 14, 20, 21, 27, 28, 34, 35, 41, 42];
-
- 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
- // RW: now here's the set
- if g_PrevDateIndex in setWE then
- Canvas.Font.Color := ColWeekend
- else
- Canvas.Font.Color := clBlack;
-
- Canvas.Font.Style := [];
- StrPCopy( pDate, g_DateArray[g_PrevDateIndex] );
-
- // RW: Check if current day is a holiday in the list
- if CheckHoliday(Holidays, pDate, FMonth) then
- Canvas.Font.Color := ColHoliday;
- if CheckHoliday(Markdays, pDate, FMonth) then
- Canvas.Font.Color := ColMarked;
-
- 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}
-
- // RW: here again the set is used
- if nIndex in setWE then
- Canvas.Font.Color := ColWeekend
- else
- Canvas.Font.Color := clBlack;
-
- Canvas.Font.Style := [fsBold];
- TempRect := GetRectFromIndex(nIndex);
- StrPCopy( pDate, g_DateArray[nIndex] );
-
- // RW: check for holiday once more
- if CheckHoliday(Holidays, pDate, FMonth) then
- Canvas.Font.Color := ColHoliday;
- if CheckHoliday(Markdays, pDate, FMonth) then
- Canvas.Font.Color := ColMarked;
-
- 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 rDayOfWeek(JDate) in [6, 7, 1] then n := 1 else n := -1;
- while rDayOfWeek(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;
-
- // RW: added these functions
- // Toggles start of the week (Sunday or Monday)
- procedure TCalenPnl.SetGermanDate(Value: Boolean);
- begin
- if Value <> FGermanDate then
- begin
- FGermanDate := Value;
- LoadDateArray;
- Refresh;
- end;
- end;
-
- // Corrected built-in-function to fit german date
- function TCalenPnl.rDayOfWeek(vDate: TDateTime) : Integer;
- begin
- Result := DayOfWeek(vDate);
- if GermanDate = True then
- begin
- Result := Result - 1; // Sonntag abziehen / subtract Sunday
- if Result = 0 then Result := 7; // Fehler ausgleichen / error correction
- end;
- end;
-
- // functions to set color values
- procedure TCalenPnl.SetColHoliday(Value: TColor);
- begin
- if Value <> FColHoliday then
- begin
- FColHoliday := Value;
- Refresh;
- end;
- end;
-
- procedure TCalenPnl.SetColWeekend(Value: TColor);
- begin
- if Value <> FColWeekend then
- begin
- FColWeekend := Value;
- Refresh;
- end;
- end;
-
- procedure TCalenPnl.SetColMarked(Value: TColor);
- begin
- if Value <> FColMarked then
- begin
- FColMarked := Value;
- Refresh;
- end;
- end;
-
- // build a string list for Holidays
- procedure TCalenPnl.SetHolidays(Value: TStrings);
- begin
- Holidays.Assign (Value);
- end;
-
- // build a string list for special days
- procedure TCalenPnl.SetMarkdays(Value: TStrings);
- begin
- Markdays.Assign (Value);
- end;
-
- // RW: this function compares a given day and month with the strings
- // of a stringlist to find out about holidays and special days
- function TCalenPnl.CheckHoliday(DateList: TStrings; sd: PChar; m: integer) : Boolean;
- var
- i, z: integer;
- scmp, sm: string;
- begin
- // Anzahl der ListeneintrΣge bestimmen
- // Determine number of listentries
- z := Datelist.Count - 1;
- Result := False;
- scmp := '';
- if (Datelist.Count > 0) and (sd <> ' ') and (m > 0) then begin
- // Vergleichsstring basteln
- // Create compare string
- Str(m, sm);
- if GermanDate = True then
- scmp:= sd + '.' + sm + '.'
- else
- scmp:= sm + '/' + sd + '/';
- // Liste durchgehen, alle EintrΣge vergleichen
- // Step through the list and compare all entries
- for i := 0 to z do begin
- if scmp = Datelist.Strings[i] then begin
- Result := True;
- break;
- end;
- end;
- end;
- end;
-
- end.
-