ggr@hudson.UUCP (Guy Riddle) (08/21/84)
# Here is Mike Schuster's desk accessory for you non-ARPA folks. # Guy #----------------------------------------------------------------------- # The desk accessory "Calendar" draws a monthly calendar above a note # pad text region containing a daily agenda. Agendas can be edited with # the standard Cut, Copy, Paste, Clear, and Undo commands. Calendar # uses the standard desktop clipboard, so you can easily transfer text # between other applications and desk accessories. Agendas for # different dates can be viewed by clicking on the various parts of the # calendar. Click on Sun, Mon, ..., or Sat to see the agenda for # another day of the week. Click on 1, 2, ..., or 31 to see the agenda # for another day of the month. Click on Jan, Feb, ..., or Dec to see # the calendar for another month. Click on 83 or 85 to see the calendar # for last year or next year. Look closely and you will see how to get # other years. Finally, click on the top region containing the date and # time to see today's agenda. # # Calendar saves each day's agenda as a purgeable resource of type # "TEXT" named "month/day/year" with some random id in a resource file # called "Calendar File". I keep "Calendar File" in "System Folder" # along with "Note Pad File" and "Clipboard File". Calendar requires # about 20K bytes, and hence it won't work under some applications, # notably MacPaint and MacTerminal. # # Calendar is compiled with Bill Croft's SUMACC as a DRVR resource in a # desk accessory launcher/installer application called "Desk". Calendar # can be used by launching "Desk" and pulling down the Apple menu or by # first copying it from Desk into your system resource file using either # "Resource Mover" or Desk's "Install" command. I assume that your # version of "Rmaker" implements resource names as well as DRVR # resources. Before compiling, check the path names defined in # "Makefile". They will have to be changed to match your environment. # # The file "device.h" contains some useful definitions for io drivers # and desk accessories. "Crtcal.s" is a slightly modified version of # SUMACC's driver/accessory self-relocator "crtdrvr.s". # # Mike Schuster (mikes@cit-20, mikes@cit-vax) # #! /bin/sh echo x - desk.c cat > desk.c << '{~}{~}' /* * Desk accessory launcher/installer. * * (C) Copyright 1984 Michael Schuster * All Rights Reserved */ #include "quickdraw.h" #include "osintf.h" #include "toolintf.h" #define NIL 0 #define FALSE 0 #define TRUE 1 #define appleMenu 1 #define fileMenu 2 #define editMenu 3 #define closeItem 1 #define installItem 2 #define quitItem 3 #define undoItem 1 #define cutItem 3 #define copyItem 4 #define pasteItem 5 #define clearItem 6 MenuHandle menus[editMenu + 1]; main() { struct QDVar QDVar; EventRecord event; WindowPtr window; Rect dragRect; QD = &QDVar; InitGraf(&thePort); InitFonts(); InitWindows(); InitMenus(); TEInit(); dragRect = QD->screenBits.bounds; menus[appleMenu] = NewMenu(appleMenu, "\24"); AddResMenu(menus[appleMenu], "DRVR"); InsertMenu(menus[appleMenu], 0); menus[fileMenu] = NewMenu(fileMenu, "File"); AppendMenu(menus[fileMenu], "Close"); AppendMenu(menus[fileMenu], "Install"); AppendMenu(menus[fileMenu], "Quit"); InsertMenu(menus[fileMenu], 0); menus[editMenu] = NewMenu(editMenu, "Edit"); AppendMenu(menus[editMenu], "Undo/Z"); AppendMenu(menus[editMenu], "(-"); AppendMenu(menus[editMenu], "Cut/X"); AppendMenu(menus[editMenu], "Copy/C"); AppendMenu(menus[editMenu], "Paste/V"); AppendMenu(menus[editMenu], "Clear/B"); InsertMenu(menus[editMenu], 0); DrawMenuBar(); SetCursor(&QD->arrow); while (TRUE) { SystemTask(); if (FrontWindow()) DisableItem(menus[fileMenu], installItem); else EnableItem(menus[fileMenu], installItem); if (!GetNextEvent(everyEvent, &event)) continue; switch (event.what) { case mouseDown: switch (FindWindow(&event.where, &window)) { case inMenuBar: SetCursor(&QD->arrow); commandEvent(MenuSelect(&event.where)); HiliteMenu(0); break; case inSysWindow: SystemClick(&event, window); break; case inDrag: DragWindow(window, &event.where, &dragRect); break; case inGoAway: if (TrackGoAway(window, &event.where)) commandEvent((fileMenu << 16) | closeItem); break; case inContent: if (window != FrontWindow()) SelectWindow(window); break; } break; case keyDown: case autoKey: if (event.modifiers & cmdKey) { commandEvent(MenuKey(event.message & 0xff)); HiliteMenu(0); } break; } } } commandEvent(menuItem) { WindowPtr window; int menu; int item; char name[64]; char *c2pnstr(); window = FrontWindow(); SetPort(window); menu = HiWord(menuItem); item = LoWord(menuItem); switch (menu) { case appleMenu: GetItem(menus[appleMenu], item, name); OpenDeskAcc(c2pnstr(name)); break; case fileMenu: switch (item) { case closeItem: CloseDeskAcc(((WindowPeek) window)->windowKind); break; case installItem: install(); break; case quitItem: ExitToShell(); break; } break; case editMenu: switch (item) { case undoItem: SystemEdit(undoCmd); break; case cutItem: SystemEdit(cutCmd); break; case copyItem: SystemEdit(copyCmd); break; case pasteItem: SystemEdit(pasteCmd); break; case clearItem: SystemEdit(clearCmd); break; } break; } } /* install local drivers as system drivers */ install() { int i; int j; int k; int rsrc; Handle oldHandle; Handle handle; Handle drivers[16]; ResType type; int id; int attrs; char name[256]; j = CountResources("DRVR"); k = 0; rsrc = CurResFile(); SetResLoad(FALSE); for (i = 1; i <= j; i++) { handle = GetIndResource("DRVR", i); if (handle) { ReleaseResource(handle); handle = GetIndResource("DRVR", i); } if (handle && HomeResFile(handle) == rsrc) drivers[++k] = handle; } SetResLoad(TRUE); for (i = 1; i <= k; i++) { UseResFile(rsrc); LoadResource(drivers[i]); handle = NewHandle(GetHandleSize(drivers[i])); if (!MemError()) { BlockMove(*drivers[i], *handle, GetHandleSize(handle)); GetResInfo(drivers[i], &id, type.s, name); attrs = GetResAttrs(drivers[i]); ReleaseResource(drivers[i]); UseResFile(0); if (oldHandle = GetResource("DRVR", id)) { UseResFile(rsrc); if (installAlert(name, "DRVR", id) == 2) continue; UseResFile(0); RmveResource(oldHandle); DisposHandle(oldHandle); } if (oldHandle = GetNamedResource("DRVR", name)) { UseResFile(rsrc); if (installAlert(name, "DRVR", id) == 2) continue; UseResFile(0); RmveResource(oldHandle); DisposHandle(oldHandle); } AddResource(handle, "DRVR", id, name); if (!ResError()) { WriteResource(handle); SetResAttrs(handle, attrs); } } else ReleaseResource(drivers[i]); } UpdateResFile(0); UseResFile(rsrc); } /* install alert */ installAlert(name, type, id) char *name; char *type; int id; { char idstring[16]; NumToString(id, idstring); ParamText(name, type, idstring, ""); return CautionAlert(256, (ProcPtr) NIL); } /* convert a C string to a Pascal string with a leading NUL character */ char *c2pnstr(s) char *s; { extern char *isapstr(); char *t; int i; int j; i = 0; while (*s++) i++; j = i; t = s - 2; while (j--) *s-- = *t--; *s-- = 0; *s = (char) i + 1; return(isapstr(s)); } {~}{~} echo x - cal.c cat > cal.c << '{~}{~}' /* * Calendar desk accessory * * (C) Copyright 1984 Michael Schuster * All Rights Reserved */ #include "quickdraw.h" #include "osintf.h" #include "toolintf.h" #include "device.h" #include "res.h" #define NIL 0 #define FALSE 0 #define TRUE 1 /* * string constants */ char TITLE[] = {"Calendar"}; /* calendar window title */ char FILE[] = {"Calendar File"};/* calendar file title */ char TEXT[] = {"TEXT"}; /* calendar resource type */ char *dayNames[] = /* names of week days */ {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; char *monthNames[] = /* names of months */ {"January", "Feburary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; /* * integer constants */ long blackPat[] = {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}; long grayPat[] = {0xaa55aa55, 0xaa55aa55, 0xaa55aa55, 0xaa55aa55}; int mLength[] = /* lengths of months (non-leap year) */ {31,31,28,31,30,31,30,31,31,30,31,30,31,31}; /* * font constants */ #define textFont Geneva /* text font */ #define calFont Monaco /* calendar font */ #define textSize 9 /* font size */ #define lines 13 /* number of lines of text */ int ascent; /* character ascent */ int descent; /* character descent */ int extent; /* character extent (width) */ int indent; /* horizontal indentation */ int leading; /* vertical spacing */ int topping; /* vertical indentation */ /* * global variables */ WindowPtr window; /* calendar window */ #define text ((TEHandle) GetWRefCon(window)) /* calendar text */ int rsrc; /* calendar resource file */ int tickTime; /* tick to draw time */ int undoCommand; /* command to undo */ Handle resources[32]; /* day resources */ #define undoCutCmd 7 #define undoCopyCmd 8 #define undoPasteCmd 9 #define undoClearCmd 10 #define typeCmd 11 #define undoTypeCmd 12 /* * date and time variables */ DateArray date; /* current date */ DateArray time; /* current time */ #define second d[DISecond] #define minute d[DIMinute] #define hour d[DIHour] #define day d[DIDay] #define month d[DIMonth] #define year d[DIYear] #define dayOfWeek d[DIDayofWeek] #define dateVal(i) d[DIVal[i]] int DIVal[] = {1, 2, 0}; /* * scrap variables */ typedef struct { short length; short filler; Handle handle; } ScrapRec; typedef ScrapRec *ScrapPtr; #define TEScrapAddr 2736 /* address of text edit scrap */ ScrapPtr TEScrap; /* text edit scrap */ Handle UDScrap; /* undo scrap */ int UDSel; /* undo scrap selection length */ int UDChars; /* undo scrap character count */ /* * global regions */ Point cellP; /* size of calendar cell */ Rect windowR; /* calendar window bounds */ Rect titleR; /* title bounds */ Rect dayR; /* day bounds */ Rect calendarR; /* calendar bounds */ Rect monthR; /* month bounds */ Rect yearR; /* year bounds */ Rect textR; /* text bounds */ Rect textClickR; /* text click bounds */ /* * string variables */ char blanks[] = {" "}; char zeros[] = {"0000000000"}; #define strLen 5 char *numToString(); char *index(); char *unParseDate(); /* * driver routines */ /* * driver open routine */ drvrOpen(pb, dce) ControlParam *pb; struct dce *dce; { if (!dce->dCtlWindow) { initRegion(); newData(dce); initScrap(); initDate(); openResFile(); } return(IOrts); } /* * driver close routine */ drvrClose(pb, dce) ControlParam *pb; struct dce *dce; { closeResFile(); disposeData(dce); return(IOrts); } /* * driver control routine */ drvrCtl(pb, dce) ControlParam *pb; struct dce *dce; { SetPort(window); switch(pb->csCode) { case accEvent: handleEvent((EventRecord *) pb->csParam); break; case accRun: TEIdle(text); if (TickCount() > tickTime) drawTime(); break; case accUndo: commandEvent(undoCommand); break; case accCut: commandEvent(cutCmd); break; case accCopy: commandEvent(copyCmd); break; case accPaste: commandEvent(pasteCmd); break; case accClear: commandEvent(clearCmd); break; } return(IOrts); } /* * driver status routine */ drvrStatus(pb, dce) ControlParam *pb; struct dce *dce; { return(IOrts); } /* * driver prime routine */ drvrPrime(pb, dce) ControlParam *pb; struct dce *dce; { return(IOrts); } /* * resource routines */ /* * (create and) open resource file */ openResFile() { FInfo fInfo; if ((rsrc = OpenResFile(FILE)) < 0) { /* create and set file info */ CreateResFile(FILE); GetFInfo(FILE, 0, &fInfo); BlockMove("ZSYS", fInfo.fdType.s, 4); BlockMove("MACS", fInfo.fdCreator.s, 4); SetFInfo(FILE, 0, &fInfo); rsrc = OpenResFile(FILE); } /* load month and date resources */ loadMonthRes(); loadDateRes(); } /* * close resource file */ closeResFile() { unLoadDateRes(); CloseResFile(rsrc); } /* * load month resources */ loadMonthRes() { register Handle handle; register int i; int j; int id; ResType type; DateArray resDate; char name[64]; /* initialize handles */ for (i = 1; i <= 31; i++) resources[i] = NIL; /* find resource handles for current month, don't load resources */ j = CountResources(TEXT); SetResLoad(FALSE); for (i = 1; i <= j; i++) { if ((handle = GetIndResource(TEXT, i)) && (HomeResFile(handle) == rsrc)) { GetResInfo(handle, &id, type.s, name); parseDate(name, &resDate); if (sameMonth(&date, &resDate)) resources[resDate.day] = handle; } } SetResLoad(TRUE); } /* * load resource for current date into text edit record */ loadDateRes() { register Handle handle; register int length; char name[64]; /* add resource if necessary */ if (!(handle = resources[date.day])) { handle = resources[date.day] = NewHandle(0); AddResource(handle, TEXT, UniqueID(TEXT), unParseDate(name, &date)); SetResAttrs(handle, GetResAttrs(handle) | ATT_PURGEABLE); } /* load resource and make unpurgeable */ LoadResource(handle); HNoPurge(handle); /* load text edit record */ TEDeactivate(text); (*text)->hText = handle; (*text)->TElength = length = GetHandleSize(handle); TECalText(text); TESetSelect(length, length, text); TEActivate(text); } /* * unload resource for current date */ unLoadDateRes() { register Handle handle; handle = resources[date.day]; if (!(*text)->TElength) { /* remove resource if no text */ RmveResource(handle); DisposHandle(handle); resources[date.day] = NIL; } else { /* write resource if some text and make purgable */ if (GetResAttrs(handle) & ATT_CHANGED) WriteResource(handle); HPurge(handle); } } /* * change resource for current date */ changeDateRes() { ChangedResource(resources[date.day]); } /* * data structure routines */ /* * new data structures */ newData(dce) struct dce *dce; { /* create window */ OffsetRect(&windowR, 40, 50); window = NewWindow((WindowPeek) NIL, &windowR, TITLE, TRUE, RDocProc, (WindowPtr) -1, TRUE, 0); OffsetRect(&windowR, -40, -50); dce->dCtlWindow = window; /* define font and create text edit record */ SetPort(window); TextFont(textFont); TextSize(textSize); ((WindowPeek) window)->windowKind = dce->dCtlRefNum; SetWRefCon(window, (int) TENew(&textR, &textR)); TextFont(calFont); } /* * dispose data structures */ disposeData(dce) struct dce *dce; { /* dispose text edit resord, make sure resource is not disposed */ (*text)->hText = NewHandle(0); TEDispose(text); /* dispose window */ DisposeWindow(window); dce->dCtlWindow = NIL; } /* * scrap routines */ /* * initialize text and undo scraps */ initScrap() { TEScrap = (ScrapPtr) TEScrapAddr; UDScrap = NewHandle(0); undoCommand = undoCmd; } /* * transfer desk scrap to text scrap */ getScrap() { int offset; SetHandleSize(TEScrap->handle, TEScrap->length = 0); if ((TEScrap->length = GetScrap(TEScrap->handle, TEXT, &offset)) < 0) SetHandleSize(TEScrap->handle, TEScrap->length = 0); } /* * transfer text scrap to desk scrap */ putScrap() { if (!ZeroScrap()) PutScrap(TEScrap->length, TEXT, *TEScrap->handle); } /* * transfer text scrap, selection, or to end of selection to undo scrap */ putUndo(scrap) { if (!scrap) { /* transfer text scrap */ SetHandleSize(UDScrap, TEScrap->length); if (MemError()) SetHandleSize(UDScrap, 0); else BlockMove(*TEScrap->handle, *UDScrap, GetHandleSize(UDScrap)); } else if (scrap > 0) { /* transfer selection */ SetHandleSize(UDScrap, (*text)->selEnd - (*text)->selStart); if (MemError()) SetHandleSize(UDScrap, 0); else BlockMove(*(*text)->hText + (*text)->selStart, *UDScrap, GetHandleSize(UDScrap)); } else { /* transfer to end of selection */ SetHandleSize(UDScrap, (*text)->selEnd); if (MemError()) SetHandleSize(UDScrap, 0); else BlockMove(*(*text)->hText, *UDScrap, GetHandleSize(UDScrap)); UDSel = (*text)->selEnd - (*text)->selStart; UDChars = GetHandleSize(UDScrap) - UDSel; } } /* * transfer undo scrap to text scrap */ getUndo() { DisposHandle(TEScrap->handle); TEScrap->length = GetHandleSize(UDScrap); TEScrap->handle = UDScrap; UDScrap= NewHandle(0); } /* * date routines */ /* * initialize current date */ initDate() { GetTime(&date); } /* * return day of week of the first day of the month */ dayOfWeek1(date) DateArray *date; { return (date->dayOfWeek - (date->day - 1) % 7 + 6) % 7; } /* * return length of month */ monthLength(date) DateArray *date; { if (date->month == 2 && ((date->year % 4 == 0 && date->year % 100 != 0) || date->year % 400 == 0)) return 29; else return mLength[date->month]; } /* * return TRUE if same month */ sameMonth(a, b) DateArray *a; DateArray *b; { return (a->year == b->year && a->month == b->month); } /* * parse date string "month/day/year" */ parseDate(name, date) char *name; DateArray *date; { register char *q; register char *p; register int i; int val; q = name; for (i = 0; i < 3; i++) { p = index(q, '/'); *p++ = 0; StringToNum(q, &val); date->dateVal(i) = val; q = p; } } /* * unparse date string "month/day/year" */ char *unParseDate(name, date) char *name; DateArray *date; { register char *p; register int i; int val; p = name; for (i = 0; i < 3; i++) { val = date->dateVal(i); NumToString(val, p); p += strlen(p); *p++ = '/'; } *--p = '\0'; return name; } /* * return number of days to the same day in a given month in the current year */ monthDelta(m) { register int delta; int curMonth; delta = 1 - date.day; curMonth = date.month; while (m != date.month) { if (m > date.month) { delta += monthLength(&date); date.month++; } else { date.month--; delta -= monthLength(&date); } } delta += min(date.day, monthLength(&date)) - 1; date.month = curMonth; return delta; } /* * return number of days to the same day in the same month of a given year */ yearDelta(y) { register int delta; register int i; int curYear; delta = 1 - date.day; curYear = date.year; while (y != date.year) { if (y > date.year) { for (i = 0; i < 12; i++) { delta += monthLength(&date); if (++date.month > 12) { date.month = 1; date.year++; } } } else { for (i = 0; i < 12; i++) { if (--date.month < 1) { date.month = 12; date.year--; } delta -= monthLength(&date); } } } delta += min(date.day, monthLength(&date)) - 1; date.year = curYear; return delta; } /* * change current date forward or backward delta days */ changeDate(delta) { int mChanged; /* unload current date */ unLoadDateRes(); /* fix day of week */ date.dayOfWeek = (date.dayOfWeek + delta + 1091) % 7 + 1; /* change date, check if month had to be changed */ mChanged = 0; while (delta) { if ((date.day + delta <= monthLength(&date)) && (date.day + delta >= 1)) { date.day += delta; delta = 0; } else if (delta > 0) { delta -= monthLength(&date) - date.day + 1; date.day = 1; if (++date.month > 12) { date.month = 1; date.year++; } mChanged = 1; } else { delta += date.day; if (--date.month < 1) { date.month = 12; date.year--; } date.day = monthLength(&date); mChanged = 1; } } /* load month if necessary */ if (mChanged) loadMonthRes(); /* load new date */ loadDateRes(); } /* * region routines */ /* * initialize regions */ initRegion() { /* Monaco font information */ ascent = 7; descent = 2; extent = 6; indent = 4; leading = 3; topping = 3; /* define sizes of regions */ SetPt(&cellP, extent * 3 + indent * 2, topping * 2 + ascent + 1); SetRect(&titleR, 0, 0, 0, topping * 4 + ascent); SetRect(&dayR, 0, 0, 7 * cellP.h, cellP.v); SetRect(&calendarR, 0, 0, 7 * cellP.h, 6 * cellP.v); SetRect(&monthR, 0, 0, 2 * cellP.h, 6 * cellP.v); SetRect(&yearR, 0, 0, 2 * cellP.h, cellP.v); /* place regions relative to one another */ OffsetRect(&dayR, extent, titleR.bottom); OffsetRect(&calendarR, extent, dayR.bottom); OffsetRect(&monthR, calendarR.right + extent + 2, titleR.bottom); OffsetRect(&yearR, calendarR.right + extent + 2, monthR.bottom); titleR.right = monthR.right + extent + 1; /* define text edit regions */ SetRect(&textR, 0, 0, ((titleR.right / extent) - 2) * extent + 1, (ascent + descent + leading) * lines); OffsetRect(&textR, (titleR.right - textR.right) / 2, calendarR.bottom + extent); /* finish up */ SetRect(&windowR, 0, 0, titleR.right, textR.bottom + extent); SetRect(&textClickR, 0, calendarR.bottom + 1, titleR.right, windowR.bottom); } /* * event routines */ /* * handle event */ handleEvent(event) EventRecord *event; { char chr; switch(event->what) { case activateEvt: activateEvent(event->modifiers & activeFlag); break; case updateEvt: updateEvent(); break; case mouseDown: GlobalToLocal(&event->where); mouseEvent(&event->where, event->modifiers); break; case keyDown: case autoKey: chr = event->message & 0xff; if (event->modifiers & cmdKey) { if (chr == 'z' || chr == 'Z') commandEvent(undoCommand); else if (chr == 'x' || chr == 'X') commandEvent(cutCmd); else if (chr == 'c' || chr == 'C') commandEvent(copyCmd); else if (chr == 'v' || chr == 'V') commandEvent(pasteCmd); else if (chr == 'b' || chr == 'B') commandEvent(clearCmd); } else keyDownEvent(chr); break; } } /* * handle activate event */ activateEvent(active) { if (active) { getScrap(); TEActivate(text); } else { TEDeactivate(text); putScrap(); } hiliteRect(&calendarR, &cellP, 7, 6, date.day + dayOfWeek1(&date) - 1); undoCommand = undoCmd; } /* * handle update event */ updateEvent() { BeginUpdate(window); if ((*text)->active) hiliteRect(&calendarR, &cellP, 7, 6, date.day + dayOfWeek1(&date) - 1); drawDate(); drawTime(); frameRect(&dayR, &cellP, 7, 1, (Pattern *) grayPat); drawTextRect(&dayR, &cellP, 7, 1, dayNames, 3); frameRect(&calendarR, &cellP, 7, 6, (Pattern *) grayPat); drawNumRect(&calendarR, &cellP, 7, 6, dayOfWeek1(&date), 1, monthLength(&date), 1, resources); frameRect(&monthR, &cellP, 2, 6, (Pattern *) grayPat); drawTextRect(&monthR, &cellP, 2, 6, monthNames, 3); frameRect(&yearR, &cellP, 2, 1, (Pattern *) grayPat); drawNumRect(&yearR, &cellP, 2, 1, 0, date.year - 1, date.year + 1, 2, (Handle *) NIL); drawText(); if ((*text)->active) hiliteRect(&calendarR, &cellP, 7, 6, date.day + dayOfWeek1(&date) - 1); EndUpdate(window); } /* * handle date event, change date either delta days or to today */ dateEvent(delta, today) { if (delta || today) { hiliteRect(&calendarR, &cellP, 7, 6, date.day + dayOfWeek1(&date) - 1); if (today) { unLoadDateRes(); GetTime(&date); loadMonthRes(); loadDateRes(); } else changeDate(delta); drawDate(); drawNumRect(&calendarR, &cellP, 7, 6, dayOfWeek1(&date), 1, monthLength(&date), 1, resources); drawNumRect(&yearR, &cellP, 2, 1, 0, date.year - 1, date.year + 1, 2, (Handle *) NIL); drawText(); hiliteRect(&calendarR, &cellP, 7, 6, date.day + dayOfWeek1(&date) - 1); } } /* * handle mouse event */ mouseEvent(point, modifiers) Point *point; { int delta; if (PtInRect(point, &textClickR)) TEClick(point, (modifiers & shiftKey) ? TRUE : FALSE, text); else if (PtInRect(point, &titleR)) { /* today */ delta = mouseRect(&titleR, &titleR.botRight, point, 1, 1, TRUE); if (delta >= 0) dateEvent(0, TRUE); } else if (PtInRect(point, &dayR)) { /* same week */ delta = mouseRect(&dayR, &cellP, point, 7, 1, TRUE); if (delta >= 0) dateEvent(delta + 1 - date.dayOfWeek, FALSE); } else if (PtInRect(point, &calendarR)) { /* same month */ delta = mouseRect(&calendarR, &cellP, point, 7, 6, TRUE); if (delta >= 0) dateEvent(delta + 1 - dayOfWeek1(&date) - date.day, FALSE); } else if (PtInRect(point, &monthR)) { /* another month */ delta = mouseRect(&monthR, &cellP, point, 2, 6, FALSE); if (delta >= 0) dateEvent(monthDelta(delta + 1), FALSE); } else if (PtInRect(point, &yearR)) { /* another year */ delta = mouseRect(&yearR, &cellP, point, 2, 1, TRUE); if (delta >= 0) dateEvent(yearDelta((delta) ? date.year + 1 : date.year - 1), FALSE); } undoCommand = undoCmd; } /* * handle key down event */ keyDownEvent(chr) { if (undoCommand != undoTypeCmd) putUndo(-TRUE); else if (chr == 8 && (*text)->selStart > 0) UDChars = min(UDChars, (*text)->selStart - 1); TEKey(chr, text); changeDateRes(); undoCommand = undoTypeCmd; } /* * handle command event */ commandEvent(cmd) { int i; switch (cmd) { case undoCmd: undoCommand = undoCmd; break; case cutCmd: putUndo(FALSE); TECut(text); putScrap(); undoCommand = undoCutCmd; break; case undoCutCmd: TEDeactivate(text); TEPaste(text); TESetSelect((*text)->selStart-TEScrap->length, (*text)->selEnd, text); TEActivate(text); getUndo(); putScrap(); undoCommand = cutCmd; break; case copyCmd: putUndo(FALSE); TECopy(text); putScrap(); undoCommand = undoCopyCmd; break; case undoCopyCmd: getUndo(); putScrap(); undoCommand = copyCmd; break; case pasteCmd: putUndo(TRUE); TEPaste(text); undoCommand = undoPasteCmd; break; case undoPasteCmd: TEDeactivate(text); TESetSelect((*text)->selStart-TEScrap->length, (*text)->selEnd, text); TEDelete(text); TEInsert(*UDScrap, GetHandleSize(UDScrap), text); TESetSelect((*text)->selStart - GetHandleSize(UDScrap), (*text)->selEnd, text); SetHandleSize(UDScrap, 0); TEActivate(text); undoCommand = pasteCmd; break; case clearCmd: putUndo(TRUE); TEDelete(text); undoCommand = undoClearCmd; break; case undoClearCmd: TEDeactivate(text); TEInsert(*UDScrap, GetHandleSize(UDScrap), text); TESetSelect((*text)->selStart - GetHandleSize(UDScrap), (*text)->selEnd, text); SetHandleSize(UDScrap, 0); TEActivate(text); undoCommand = clearCmd; break; case typeCmd: TEDeactivate(text); TESetSelect((*text)->selEnd, (*text)->selEnd, text); i = GetHandleSize(UDScrap); TEInsert(*UDScrap, i, text); TESetSelect(0, (*text)->selEnd - i, text); putUndo(TRUE); TESetSelect(UDChars, (*text)->selEnd, text); TEDelete(text); TESetSelect((*text)->selEnd + i, (*text)->selEnd + i, text); TEActivate(text); undoCommand = undoTypeCmd; break; case undoTypeCmd: TEDeactivate(text); TESetSelect(UDChars, (*text)->selEnd, text); TEInsert(*UDScrap + UDChars, GetHandleSize(UDScrap) - UDChars, text); putUndo(TRUE); TEDelete(text); TESetSelect((*text)->selEnd - UDSel, (*text)->selEnd, text); TEActivate(text); undoCommand = typeCmd; break; } changeDateRes(); } /* * drawing routines */ /* * frame rectangle, draw horizontal and vertical lines * rect defines the rectangle * point defines the size of each cell within the rectangle * nh defines the number of columns * nv defines the number of rows * pat defines the pattern */ frameRect(rect, point, nh, nv, pat) Rect *rect; Point *point; Pattern *pat; { register int i; register d; PenPat(pat); /* draw horizontal lines */ d = point->h * nh; MoveTo(rect->left, rect->top); for (i = 0; i <= nv; i++) { Line(d, 0); Move(-d, point->v); } /* draw vertical lines */ d = point->v * nv; MoveTo(rect->left, rect->top); for (i = 0; i <= nh; i++) { Line(0, d); Move(point->h, -d); } PenPat((Pattern *) blackPat); } /* * draw text in rectangle * rect defines the rectangle * point defines the size of each cell within the rectangle * nh defines the number of columns * nv defines the number of rows * txt defines the text to be draw into each cell * n defines the number of characters of text to draw */ drawTextRect(rect, point, nh, nv, txt, n) Rect *rect; Point *point; char **txt; { register int i; register int j; register int k; register int d; TextFont(calFont); MoveTo(rect->left + indent + 1 + ((3 - n) * extent) / 2, rect->top + topping + ascent + 1); k = 0; d = n * extent; for (i = 0; i < nh; i++) { for (j = 0; j < nv; j++) { DrawText(txt[k++], 0, n); Move(-d, point->v); } Move(point->h, -(nv * point->v)); } } /* * draw number rectangle * rect defines the rectangle * point defines the size of each cell within the rectangle * nh defines the number of columns * nv defines the number of rows * numbers min, min+delta, min+2*delta, ..., max are drawn into cells * disp defines the initial number of cells to leave blank * hilite defines which cell to hilite */ drawNumRect(rect, point, nh, nv, disp, min, max, delta, hilite) Rect *rect; Point *point; Handle *hilite; { Rect bounds; register int i; register int j; register int k; int d; Handle *hi; TextFont(calFont); SetRect(&bounds, 1, 1, point->h, point->v); OffsetRect(&bounds, rect->left, rect->top); MoveTo(rect->left + indent + 1 + extent / 2, rect->top + topping + ascent + 1); k = min - disp * delta; d = 2 * extent; hi = (hilite) ? &hilite[min] : NIL; for (i = 0; i < nv; i++) { for (j = 0; j < nh; j++) { EraseRect(&bounds); if (k >= min && k <= max) { TextFace((hi && *hi) ? outlineStyle : 0); if (hi && *hi) Move(-1, 0); DrawString(numToString(k, 2)); if (hi && *hi) Move(-1, 0); Move(point->h - d, 0); (hi) ? hi++ : 0; } else Move(point->h, 0); k += delta; OffsetRect(&bounds, point->h, 0); } Move(-(nh * point->h), point->v); OffsetRect(&bounds, -(nh * point->h), point->v); } TextFace(0); } /* * mouse rectangle, return cell number * rect defines the rectangle * point defines the size of each cell within the rectangle * where defines the initial location of the mouse * nh defines the number of columns * nv defines the number of rows * hori defines if cells are to be counted horizontally or vertically */ mouseRect(rect, point, where, nh, nv, hori) Rect *rect; Point *point; Point *where; { Rect bounds; int i; int j; int invert; SetRect(&bounds, 0, 0, point->h + 1, point->v + 1); OffsetRect(&bounds, (i = (where->h - rect->left) / point->h) * point->h + rect->left, (j = (where->v - rect->top) / point->v) * point->v + rect->top); InvertRect(&bounds); invert = TRUE; while (StillDown()) { GetMouse(where); if (invert != PtInRect(where, &bounds)) { invert = TRUE - invert; InvertRect(&bounds); } } if (invert) { InvertRect(&bounds); return (hori) ? j * nh + i : i * nv + j; } else return -1; } /* * hilite a cell in a rectangle * rect defines the rectangle * point defines the size of each cell within the rectangle * nh defines the number of columns * nv defines the number of rows * n defines the number of the cell to hilite */ hiliteRect(rect, point, nh, nv, n) Rect *rect; Point *point; { Rect bounds; SetRect(&bounds, 1, 1, point->h, point->v); OffsetRect(&bounds, rect->left, rect->top); OffsetRect(&bounds, point->h * (n % nh), point->v * (n / nh)); InvertRect(&bounds); } /* * draw text */ drawText() { /* wasteful, but necessary if TECalText used on shorter text */ EraseRect(&textR); /* update */ TextFont(textFont); TEUpdate(&textR, text); } /* * draw date */ drawDate() { Rect bounds; TextFont(calFont); SetRect(&bounds, 0, 0, extent * 28, ascent + descent); OffsetRect(&bounds, extent, 2 * topping); EraseRect(&bounds); MoveTo(extent, 2 * topping + ascent); DrawText(dayNames[date.dayOfWeek - 1], 0, strlen(dayNames[date.dayOfWeek - 1])); DrawChar(' '); DrawText(monthNames[date.month - 1], 0, strlen(monthNames[date.month - 1])); DrawChar(' '); DrawString(numToString(date.day, 0)); DrawChar(','); DrawString(numToString(date.year, 0)); } /* * draw time, set up tickTime for next minute update */ drawTime() { Rect bounds; TextFont(calFont); SetRect(&bounds, 0, 0, extent * 9, ascent + descent); OffsetRect(&bounds, titleR.right - 9 * extent + 1, 2 * topping); EraseRect(&bounds); MoveTo(titleR.right - 9 * extent + 1, 2 * topping + ascent); GetTime(&time); tickTime = TickCount() - time.second * 60 + 3600; DrawString(numToString((time.hour + 11) % 12 + 1, 2)); DrawChar(':'); DrawString(numToString(time.minute, -2)); DrawChar(' '); DrawText("AMPM", (time.hour < 12) ? 0 : 2, 2); } /* * utility routines */ /* * return minimum */ min(a, b) { return (a < b) ? a : b; } /* * return length of string */ strlen(s) char *s; { register char *p = s; register int n = 0; while (*p++) n++; return(n); } /* * return position of character */ char *index(s, c) char *s; char c; { register char *p = s; register char n = c; while (*p && *p != n) p++; return(p); } /* * convert number to string * w == 0 -> no leading blanks or zeros * w > 0 -> add leading blanks to make string length equal to w * w < 0 -> add leading zeros to make string length equal to -w */ char *numToString(n, w) { if (!w) { NumToString(n, &blanks[strLen]); return &blanks[strLen]; } else if (w > 0) { NumToString(n, &blanks[strLen]); return &blanks[strLen - w + strlen(&blanks[strLen])]; } else { NumToString(n, &zeros[strLen]); return &zeros[strLen + w + strlen(&zeros[strLen])]; } } {~}{~} echo x - crtcal.s cat > crtcal.s << '{~}{~}' | | crtcal.s - self relocating C runtime startoff for Mac desk accessory | | Copyright (C) 1984, Stanford Univ. SUMEX project | May be used but not sold without permission. | | history | 07/20/84 Croft Created. | 07/26/84 Schuster Customize for Calendar. | .data .text .globl _savea5 .globl drvr .globl drvrOpen,drvrPrime,drvrCtl,drvrStatus,drvrClose | driver header drvr: .word 0x2400 | enable control, need time .word 15 | every 1/4 second .word 0x016a | activate, update, autokey, keydown, mousedown .word 0 | menu doffset: .word reloc-drvr | replaced by "dopen-drvr" after initialization .word dprime-drvr .word dctl-drvr .word dstatus-drvr .word dclose-drvr .byte 9 .ascii "\0Calendar" .blkb 22 | 32 bytes total for name reloc: jra .L21 .long 0,0,0,0,0,0,0,0,0,0 | longruns from rmaker _savea5:.long 0 | | a1 = next longrun address | a2 = current reloc address | d1 = relocation factor | .L21: moveml #0xffff,sp@- lea pc@([drvr-.-2]),a1 | reloc factor movl a1,d1 lea pc@([reloc+2-.-2]),a1 movl a1@+,a2 | pickup 1st relocation addl d1,a2 .L16: | for(;;) { | i = *a2; | *a2 = 0; | *(u_long *)a2 += (u_long)d1; | if (i == 0377) | goto start; | if (i == 0) { | a2 = *a1++; | a2 += d1; | continue; | } | a2 += (i << 1); | } movb a2@,d7 andl #255,d7 clrb a2@ addl d1,a2@ cmpl #255,d7 beqs .L18 tstl d7 bnes .L19 movl a1@+,a2 addl d1,a2 bras .L16 .L19: roll #1,d7 addl d7,a2 bras .L16 | | if shift button is pressed on entry, beep and hang around for an NMI. | .L18: btst #0,0x17b beqs .L31 | if not pressed movw #1,sp@- | sysbeep, duration 1 .word /A9C8 moveq #1,d0 .L22: tstl d0 bnes .L22 | hang, waiting for NMI .L31: movl a5,_savea5 movw doff2,doffset | above code is once-only moveml sp@+,#0xffff | | driver entry points | dopen: movl #drvrOpen,d0 bras call dclose: movl #drvrClose,d0 bras call dctl: movl #drvrCtl,d0 bras call dstatus: movl #drvrStatus,d0 bras call dprime: movl #drvrPrime,d0 call: moveml #0x3ffc,sp@- movl a1,sp@- movl a0,sp@- movl d0,a0 jsr a0@ addql #8,sp moveml sp@+,#0x3ffc cmpl #0x40000000,d0 bnes done clrl d0 rts jiodone = 0x8fc done: movl jiodone,sp@- rts doff2: .word dopen-drvr {~}{~} echo x - device.h cat > device.h << '{~}{~}' /* device.h 1.0 07/21/84 */ /* * Device driver definitions. */ /* * Copyright (C) 1984, Stanford Univ. SUMEX project. * May be used but not sold without permission. */ /* * history * 07/21/84 Croft Created. * 07/27/84 Schuster Added control/status parameter structure. */ /* * Driver structure. */ struct drvr { short drvrFlags; /* flags */ short drvrDelay; /* ticks between actions */ short drvrEMask; /* desk accessory event mask */ short drvrMenu; /* menu ID */ short drvrOpen; /* offset to open routine */ short drvrPrime; /* .. prime */ short drvrCtl; /* .. control */ short drvrStatus; /* .. status */ short drvrClose; /* .. close */ char drvrName[32]; /* driver name (pascal string) */ }; struct drvr drvr; /* global instance of my drvr struct */ /* flags in drvrFlags */ #define dReadEnable 0x100 #define dWritEnable 0x200 #define dCtlEnable 0x400 #define dStatEnable 0x800 #define dNeedGoodBye 0x1000 #define dNeedTime 0x2000 #define dNeedLock 0x4000 #define dOpened 0x20 #define dRAMBased 0x40 #define drvrActive 0x80 /* * Device control entry structure. */ struct dce { Handle dCtlDriver; /* pointer/handle to driver */ short dCtlFlags; short dCtlQueue; Ptr dCtlQHead; /* 1st entry in IO queue */ Ptr dCtlQTail; long dCtlPosition; /* offset used by R/W calls */ Handle dCtlStorage; /* private storage */ short dCtlRefNum; /* driver's refnum */ long dCtlCurTicks; /* current tick count (kept by Device Mgr) */ WindowPtr dCtlWindow; /* window record (if any) */ short dCtlDelay; /* ticks between actions */ short dCtlEMask; /* event mask (desk acc) */ short dCtlMenu; /* menu ID */ }; /* * Commands in (low byte of) ioTrap. */ #define aRdCmd 2 #define aWrCmd 3 #define aCtlCmd 4 #define aStsCmd 5 /* * Special return codes from driver entry points. * * A zero or negative return from the driver routines causes an IOdone, * with the return status in D0. The return values below (large * positive numbers) cause the indicated action. */ #define IOrts 0x40000000 #define IOkill IOrts #define IOdone 0 /* * Control and status codes. */ #define KillCode 1 #define EjectCode 7 #define DrvStsCode 8 /* * IO System Errors (should be in osintf.h) */ #define ControlErr -17 #define StatusErr -18 #define ReadErr -19 #define WritErr -20 #define BadUnitErr -21 #define UnitEmptyErr -22 #define OpenErr -23 #define ClosErr -24 #define DRemovErr -25 #define DInstErr -26 #define AbortErr -27 #define NotOpenErr -28 struct ControlParam { /* control/status parameter */ struct ioParam *ioLink; short ioType; /* = IOQType */ short ioTrap; /* trap code */ Ptr ioCmdAddr; /* address to dispatch to */ ProcPtr ioCompletion; /* completion routine */ short ioResult; char *ioNamePtr; /* vol:filename string */ short ioVRefNum; /* volume refnum (or drvnum) */ short filler2; short csCode; /* control/status code */ long csParam; /* control/status parameters */ }; typedef struct ControlParam ControlParam; /* * Control and Status codes for csCode */ #define accEvent 64 #define accRun 65 #define accCursor 66 #define accMenu 67 #define accUndo 68 #define accCut 70 #define accCopy 71 #define accPaste 72 #define accClear 73 {~}{~} echo x - desk.rc cat > desk.rc << '{~}{~}' desk.rsrc Type ALRT ,256(32) 60 81 180 431 256 5555 Type DITL ,256(32) 3 BtnItem Enabled 90 10 110 80 OK BtnItem Enabled 90 270 110 340 Cancel StatText Disabled 10 60 70 350 Replace system resource ^0 type=^1 id=^2? Type DRVR cal|Calendar,31(48) Type CODE desk,0 {~}{~} echo x - Makefile cat > Makefile << '{~}{~}' .SUFFIXES: .rsrc .b .ln .s .c BIN=/usr1/mac/bin/ INCLUDE=/usr1/mac/include CFILES = desk.c cal.c HFILES = device.h SFILES = crtcal.s .c.b: $(BIN)cc68 -I$(INCLUDE) -c $< .c.s: $(BIN)cc68 -I$(INCLUDE) -S $< .s.b: $(BIN)cc68 -c $< .c.ln: $(BIN)lint -I$(INCLUDE) -lmac $< > $*.ln cal: cal.b crtcal.b $(BIN)ld68 -X -r -d -e drvr -T 0 crtcal.b cal.b -lmac -lc -x -o cal desk: desk.b $(BIN)cc68 -m desk.b -o desk desk.rsrc: cal desk desk.rc $(BIN)rmaker desk.rc all: desk.rsrc put: desk.rsrc $(BIN)macput -r desk lint: cal.ln desk.ln desk.shar: $(CFILES) $(SFILES) $(HFILES) csh shar desk.shar $(CFILES) $(SFILES) $(HFILES) desk.rc Makefile clean: rm -f *.b *.rsrc cal desk {~}{~}