[= Simple Calendar This version of the calendar just prints out the basic set of months. [dirpath]grarls cal.grs [year] Recomented size 720x800 pixels. Remember you can set the language with the -lng option. =] [= set some display values =] Const Colour LIGHT_GREEN = {50, 100, 50}; Const TFont MainFont = Font { size -> 18 }; [= various global variables for the layout =] Number YearHeight; [= height of the year title =] Number DateWidth; [= width of individual date field =] Number DateHeight; [= height the normal text =] Number MonthWidth; [= width of a single month =] Number MonthGutter; [= horizonal space between months =] Number MonthHeight; [= Total height of month - month name, day names and data =] Number AbsRow; [= absolute row number =] [= PROGRAM ------- The main progam entry point. Takes an optional year parameter. =] Program(Number year = Null) Logical ok; Begin [= if the year is not specified then set to current year =] If year == Null Then year := GetYear(Now()); EndIf; [= if year is out of range generate an error =] If year < 1 Or year > 9999 Then Error OutOfRange, "Bad year number " + Format(year); EndIf; [= set up the page canvas layout variables =] ok := Setup(MainFont, Canvas); If Not ok Then [= setup will already have reported the error =] Return; EndIf; [= can now draw the calendar using the main font =] Calendar(year) => MainFont; End; [= CALENDAR -------- This will draw the actual calendar. The caller should have done all the checking so none needs to be done here. =] Shape Calendar(Number year) Number month; Point corner; Begin [= write ot the year title =] YearTitle(year); AbsRow := 0; [= go through all the months =] For month From 1 To 12 Do [= sort out where to draw the month =] corner := CalcCorner(month); [= draw one month =] OneMonth(year, month) => corner; EndFor; End; [= ONE MONTH --------- This will draw one month of the calendar. Most of this concerns layout calcuations and in is outside the scope of teaching the system itself. Origin: the top left corner of the month block. =] Shape OneMonth(Number year, Number month) Number fistcol, monlen, dofm, dofw, row; Number centre; Point offset; Time date; Text str; [= font for the month name - this could have been global or passed as a parameter =] TFont namefont = Font { weight -> 75 }; Begin [=get the days in this month =] monlen := MonthLen(year, month); [= find where to centr the month - not obvious but correct ( remember the spaces ) =] centre := DateWidth * 5; [= if not starting on Monday need to skip forward one row =] If GetWeekday(MakeDate(year, month, 1)) <> 1 Then AbsRow += 1; EndIf; [= create a Time object for the first of the month =] date := MakeDate(year, month, 1); [= get the day of week for the first =] dofw := GetWeekday(date) - 1; [= get the month name =] str := Format(date, "N19d"); TextBlock(str, halign -> Centre) => { centre , 0}, namefont; row := 1; [= draw the abreviated day names =] DayTitles() => { 0, row * DateHeight}; row += 1; [= go through the whole month =] For dofm From 1 To monlen Do [= format and draw the day of month number =] str := Format(dofm, "I2r"); offset.x := dofw * DateWidth * 3 / 2 + DateWidth; offset.y := row * DateHeight; TextBlock(str, halign -> Right) => offset; [= see if we need to advance the the line =] If dofw < 6 Then dofw += 1; Else dofw := 0; row += 1; AbsRow += 1; EndIf; EndFor; End; [= SETUP ----- This will set up the global layout variables based on page size and font size. This version assumes that the display will be big enough for the whole calendar. =] Function Logical Setup(TFont font, TCanvas canvas) Text str; Point textsize; Number dow; Begin [= format the widest =] str := Format(22, "I2r"); textsize := TextSize(str, font); DateWidth := textsize.x; DateHeight := textsize.y; [= 7 * fieldwidth + 6 * halfwidth =] MonthWidth := DateWidth * 10; [= space split between 2 gutter and 2 halfwidth margins =] MonthGutter := ( canvas.width - 3 * MonthWidth ) / 4; [= calculate hieght of month block =] MonthHeight := 9 * DateHeight; [= got heres so nothing went wrong =] Return True; OnError [= report the error and tell caller the routine failed =] Output Status; Return False; End; [= DAY TITLES ---------- Draw the line of day titles. "Mo", "Tu" etc ( but in the current language ). Origin: top left corner of the day name row. =] Shape DayTitles() Time date = '2000-01-01'; [= any arbitrary date will do =] Number day, dow, centre; Text dayname; TFont dayfont = Font { underline -> True }; Begin [= go through seven consecutive days ( does not have to start on Monday ) =] For day From 1 To 7 Do [= format weekday as 2 letters in default mode =] dayname := Format(date, "W2d"); [= get the days of week =] dow := GetWeekday(date); [= work out where to centre it =] centre := ( dow - 1) * 3 * DateWidth / 2 + DateWidth / 2; [= write it centred =] TextBlock(dayname, halign -> Centre) => { centre , 0}, dayfont; [= and onto the next day =] date := AddDays(date, 1); EndFor; End; [= YEAR TITLE ---------- Write the year number at the top. =] Shape YearTitle(Number year) Number yheight = Font.size * 8; TFont yearfont; Text yearstr; Begin [= create the year font =] yearfont := Font { size -> yheight, weight -> 75 }; [= write the year =] yearstr := Format(year); TextBlock(yearstr, halign -> Centre) => { Canvas.width / 2 , 0 }, Pen { colour -> LIGHT_GREEN }, yearfont; [= set the offset for the heading =] YearHeight := yheight + Font.size; End; [= CALC CORNER ----------- Calculate coordinate of the top left corner of the specified month. Result in absolute coordinates. =] Function Point CalcCorner(Number month) Point res; Number mont; Begin [= get coordinate in term of month blocks =] res.x := ( month - 1 ) Mod 3; res.y := ( month - 1 ) // 3; [= adjusts for size of block anf ovrall offsets =] res.x := res.x * ( MonthWidth + MonthGutter ) + MonthGutter; res.y := res.y * MonthHeight + YearHeight; [= return the final absolute coodinate =] Return res; End; [= IS LEAPYEAR ----------- See if the specified year as a leapyear. =] Function Logical IsLeapyear(Number year) Begin [= see if a century =] If year Mod 100 == 0 Then [= centuries must be mutiples of 400 =] Return year Mod 400 == 0; Else [= other years must be multiple of 4 =] Return year Mod 4 == 0; EndIf; End; [= MONTH LEN --------- Calculate the lenght of the specified month. =] Function Number MonthLen(Number year, Number month) Begin [= different months have different lengths =] Select month From Case 1, 3, 5, 7, 8, 10, 12 Do [= long months =] Return 31; Case 4, 6, 9, 11 Do [= short month =] Return 30; Case 2 Do [= Febraury - special case =] If IsLeapyear(year) Then Return 29; Else Return 28; EndIf; EndSelect; Return 0; [= should never reach here =] End;