Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
DrawCalendar.c
/* |
File: DrawCalendar.c |
Contains: Module for displaying the calendar (Gregorian). |
Written by: Martin Minow |
Copyright: © 1994-1997 by Apple Computer, Inc., all rights reserved. |
Change History (most recent first): |
You may incorporate this sample code into your applications without |
restriction, though the sample code has been provided "AS IS" and the |
responsibility for its operation is 100% yours. However, what you are |
not permitted to do is to redistribute the source as "DSC Sample Code" |
after having made changes. If you're going to re-distribute the source, |
we require that you make it clear in the source that the code was |
descended from Apple Sample Code, but that you've made changes. |
DrawCalendar displays the calendar (Gregorian) for the selected date. The |
algorithm has been simplified and consequently will only work for dates within |
the Macintosh epoch. |
Note that DrawCalendar is common to the Control Strip and Setup application. |
If you change it, you must rebuild both modules. |
*/ |
///////////////////////////////////////////////////////////////////////// |
// Pick up some common stuff, specifically the height and width macros. |
#include "MacCalendarCommon.h" |
///////////////////////////////////////////////////////////////////////// |
// Pick up prototypes for our exported routines. |
#include "DrawCalendar.h" |
///////////////////////////////////////////////////////////////////////// |
// Pick up system types. |
#include <Fonts.h> |
#include <IntlResources.h> |
#include <Memory.h> |
#include <OSUtils.h> |
#include <Packages.h> |
#include <QuickDraw.h> |
#include <Script.h> |
#include <TextUtils.h> |
///////////////////////////////////////////////////////////////////////// |
// Some global constants. |
#define kFebruary 2 /* The magic month */ |
/* |
* This character vector contains the number of days in a month. Because compilers |
* do not necessarily allow pc-relative addressing of generic vectors, we define |
* it as a character string. |
* 034 28 |
* 035 29 |
* 036 30 |
* 037 31 |
*/ /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ |
#define kDayInMonth "\000\037\034\037\036\037\036\037\037\036\037\036\037" |
///////////////////////////////////////////////////////////////////////// |
static short GetFontNumber( |
SavedSettingsHandle settings /* Current font etc */ |
) |
{ |
Str255 fontName; |
short fontNumber; |
pstrcpy(fontName, (**settings).fontName); |
GetFNum(fontName, &fontNumber); |
return (fontNumber); |
} |
///////////////////////////////////////////////////////////////////////// |
// GetCalendarDisplaySize |
// Return the width and height of a rectangle needed to display the calendar in the |
// specified font and font size. This is needed to draw the calendar and to position |
// the calendar display with respect to the Control Strip. |
Point GetCalendarDisplaySize( |
SavedSettingsHandle settings /* Current font etc */ |
) |
{ |
FontInfo fontInfo; |
short dateWidth; |
short lineHeight; |
Point result; |
short saveTextFont; |
short saveTextSize; |
short saveTextFace; |
GrafPtr currentPort; |
GetPort(¤tPort); |
saveTextFont = currentPort->txFont; |
saveTextSize = currentPort->txSize; |
saveTextFace = currentPort->txFace; |
TextFont(GetFontNumber(settings)); |
TextSize((**settings).fontSize); |
TextFace(normal); |
GetFontInfo(&fontInfo); |
lineHeight = fontInfo.ascent + fontInfo.descent + fontInfo.leading; |
dateWidth = StringWidth("\p 00"); |
result.h = (dateWidth * 7) + 4; /* 7 == days in the week */ |
result.v = (lineHeight * 8) + 4; /* 8 == lines of text in the calendar */ |
TextFont(saveTextFont); |
TextSize(saveTextSize); |
TextFace(saveTextFace); |
return (result); |
} |
///////////////////////////////////////////////////////////////////////// |
// GetCalendarMonthRect |
// |
// Given a display rectangle, font, and font size, create the actual display area. |
void GetCalendarMonthRect( |
SavedSettingsHandle settings, /* Current font etc */ |
const Rect *displayRect, /* Where to draw the text */ |
Rect *monthRect /* Returns drawing rect */ |
) |
{ |
Point monthSize; |
monthSize = GetCalendarDisplaySize(settings); |
/* |
* Center the month rectangle within the drawing rectangle. |
* >> 1 is used to divide by two without loading a library routine. |
*/ |
monthRect->left = displayRect->left |
+ ((width(*displayRect) - monthSize.h) >> 1); |
monthRect->top = displayRect->top |
+ ((height(*displayRect) - monthSize.v) >> 1); |
monthRect->right = monthRect->left + monthSize.h; |
monthRect->bottom = monthRect->top + monthSize.v; |
} |
///////////////////////////////////////////////////////////////////////// |
// DrawCalendar |
// |
// Draw the month - the port is set to the drawing port. Text font, size, and style |
// are not preserved. dayName is a Pascal string with the following format, repeated |
// seven times, once for each day: |
// { nByte, Byte1, Byte2, etc }. For example, if dates are represented |
// by "S M Tu W Th F S", dayName would be specified in a pascal string as |
// "\p\001S\001M\002Tu\001W\002Th\001F\001S\000" |
// Note: the day names must correspond to the firstDayOfWeek parameter. I.e. if the |
// first day is Monday, the first word in the string is "M". |
void DrawCalendar( |
SavedSettingsHandle settings, /* Current font etc */ |
short year, /* 1904 .. */ |
short month, /* January == 1 */ |
const Rect *displayRect /* Where to draw the text */ |
) |
{ |
DateTimeRec now; |
unsigned long nowSeconds; |
short weekday; |
short daysInMonth; |
short today; |
short lineHeight; |
short dateWidth; |
short spaceWidth; |
short digitWidth; |
FontInfo fontInfo; |
Rect monthRect; |
short hPos; |
short vPos; |
register unsigned char *dayNamePtr; |
short dayWidth; |
Intl1Hndl intlHdl; |
Str255 work; |
Boolean isThisMonth; |
short thisDate; |
Rect dayRect; |
PenState penState; |
short hOffset; |
short vOffset; |
short penSize; |
/* |
* Get the drawing parameters for this month. This duplicates the logic of |
* GetCalendarDisplaySize above. |
*/ |
GetCalendarMonthRect(settings, displayRect, &monthRect); |
TextFont(GetFontNumber(settings)); |
TextSize((**settings).fontSize); |
TextFace(normal); |
GetFontInfo(&fontInfo); |
lineHeight = fontInfo.ascent + fontInfo.descent + fontInfo.leading; |
spaceWidth = CharWidth(' '); |
digitWidth = CharWidth('0'); |
dateWidth = spaceWidth + (digitWidth * 2); |
/* |
* If we're displaying the current month, we want to hilite today's date. |
* 1.0d3 |
*/ |
GetDateTime(&nowSeconds); |
SecondsToDate(nowSeconds, &now); |
isThisMonth = (year == now.year && month == now.month); |
thisDate = now.day; |
/* |
* Get the parameters for this particular month. We convert day 1 to seconds, |
* then back to the date in order to locate the weekday corresponding to the |
* first day of the month. |
*/ |
now.year = year; |
now.month = month; |
now.day = 1; |
now.hour = 0; |
now.minute = 0; |
now.second = 0; |
DateToSeconds(&now, &nowSeconds); |
SecondsToDate(nowSeconds, &now); |
vPos = monthRect.top + 2 + fontInfo.ascent; |
/* |
* Draw the month and year names. |
*/ |
intlHdl = (Intl1Hndl) GetIntlResource(1); |
if (intlHdl != NULL) { |
/* |
* 1.0d4: Don't modify the actual data. |
*/ |
if (HandToHand((Handle *) &intlHdl) == noErr) { |
/* |
* Convert the date to "month, year" (myd + supress day) |
*/ |
(**intlHdl).suppressDay = 3; |
IUDatePString(nowSeconds, myd, work, (Handle) intlHdl); |
hPos = monthRect.left + 2 |
+ ((width(monthRect) - StringWidth(work)) >> 1); |
MoveTo(hPos, vPos); |
DrawString(work); |
vPos += lineHeight; |
} |
DisposeHandle((Handle) intlHdl); |
} |
/* |
* Draw the days in the week. dayName is a vector of Pascal strings hiding |
* inside a Pascal string. |
*/ |
pstrcpy(work, (**settings).dayNameString); |
dayNamePtr = (unsigned char *) &work[1]; |
hPos = monthRect.left + 2; |
TextFace(bold); /* 1.0d3 */ |
for (weekday = 0; weekday < 7; weekday++) { |
dayWidth = StringWidth((StringPtr) dayNamePtr); |
MoveTo(hPos + dateWidth - dayWidth, vPos); |
DrawString((StringPtr) dayNamePtr); |
dayNamePtr += dayNamePtr[0] + 1; |
hPos += dateWidth; |
} |
TextFace(normal); /* 1.0d3 */ |
vPos += lineHeight; |
/* |
* How far do we go in this month, with a leap year hack. |
*/ |
daysInMonth = kDayInMonth[month]; |
if ((year & 0x3) == 0 && month == kFebruary) |
++daysInMonth; |
/* |
* now.dayOfWeek is the weekday corresponding to the first day of the month. |
* For example, if the first day of the month is on a Sunday, now.dayOfWeek |
* will equal one. firstDayOfWeek will equal one for Sunday, two for Monday. |
*/ |
weekday = now.dayOfWeek - (**settings).firstDayOfWeek; |
if (weekday < 0) |
weekday = 6; |
hPos = monthRect.left + 2 + (weekday * dateWidth); |
for (today = 1; today <= daysInMonth; today++, weekday++) { |
if (weekday >= 7) { /* Wrap around to a new week. */ |
hPos = monthRect.left + 2; |
vPos += lineHeight; |
weekday = 0; |
} |
/* |
* hOffset locates the left edge of the date -- this will cover one |
* digit for dates 1 to 9, and two digits for 10 to 31. |
*/ |
hOffset = hPos + spaceWidth; |
if (today < 10) { |
hOffset += digitWidth; /* Space over the leftmost digit space */ |
MoveTo(hOffset, vPos); |
DrawChar(today + '0'); |
} |
else { |
MoveTo(hOffset, vPos); |
DrawChar((today / 10) + '0'); |
DrawChar((today % 10) + '0'); |
} |
if (isThisMonth && today == thisDate) { /* 1.0d3 */ |
/* |
* We are drawing an oval around this date. There is quite a bit of |
* eyeball adjustment that could be re-adjusted by someone with |
* more (or less) visual taste. |
*/ |
GetPenState(&penState); |
penSize = (fontInfo.ascent >= 12) ? fontInfo.ascent / 6 : 1; |
vOffset = vPos - fontInfo.ascent - penSize; |
PenSize(penSize, penSize); |
SetRect( |
&dayRect, |
hOffset - penSize, |
vOffset, |
hPos + dateWidth + penSize, |
vOffset + lineHeight + penSize |
); |
FrameRoundRect( |
&dayRect, |
(dateWidth * 3) / 4, |
(lineHeight * 3) / 4 |
); |
SetPenState(&penState); |
} |
hPos += dateWidth; |
} |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14