On our first Valentine’s Day Melissa was mad at me for some reason. So for our second Valentine’s Day I decided to make her a really special gift. I stayed up until about 2-3 a.m. (which was quite rare, as I like to get to bed early) the night before writing a computer program that made a heart shaped window with a customized message in it. With the state of software in 1998 this was actually a pretty challenging task, but I worked hard and made something I thought we could both be proud of.
When I showed it to Melissa the next day (2/14/1998) I didn’t quite get the reaction I was hoping for. Her attitude was basically that if I hadn’t spent so much time working on the program I would have had more time to spend with her. Sheesh.
Anyway, here’s what it looked like:
And here’s the source code (wow I used to write some really ugly code):
// // HeartWDEF.c // #define HASGOAWAY 0 pascal long main(short, CWindowPtr, short, long); long DoDraw(CWindowPtr, long); long DoHit(CWindowPtr, long); long DoCalcRgns(CWindowPtr, long); void GetStrucRegion(CWindowPtr window, RgnHandle rgn); void GetGoAwayBox(CWindowPtr w, Rect *r); OSErr PictToRgn(PicHandle pic, RgnHandle rgn){ int width = 0, height = 0; GrafPtr mask, savePort; Rect r = (*pic)->picFrame; OSErr err; width = r.right - r.left; height = r.bottom - r.top; SetRect(&r, 0, 0, width, height); //OffsetRect(&r, r.left, r.top); mask = (GrafPtr) NewPtr(sizeof(GrafPort)); OpenPort(mask); mask->portBits.bounds = r; mask->portBits.rowBytes = ((width + 15) / 8); mask->portBits.baseAddr = NewPtr(mask->portBits.rowBytes * height); GetPort(&savePort); SetPort(mask); EraseRect(&r); DrawPicture(pic, &r); SetPort(savePort); err = BitMapToRegion(rgn, &(mask->portBits)); DisposePtr((Ptr) mask->portBits.baseAddr); DisposePtr((Ptr) mask); return err; } pascal long main(short varCode, CWindowPtr w, short message, long param){ long val = 0; switch(message){ case wDraw: val = DoDraw(w, param); break; case wHit: val = DoHit(w, param); break; case wCalcRgns: val = DoCalcRgns(w, param); break; case wNew: case wDispose: break; } return val; } long DoDraw(CWindowPtr w, long param){ long val = 0; WindowPeek wp; Rect r; RGBColor save; PicHandle pict; Point p; WindowPtr window; GWorldPtr saveWorld; GDHandle device; PenState savePen; GetForeColor(&save); GetPenState(&savePen); PenNormal(); wp = (WindowPeek) w; if(param == 0){ // copy the regions so we can offset them RgnHandle diff = NewRgn(); DiffRgn(wp->strucRgn, wp->contRgn, diff); ForeColor(redColor); PaintRgn(diff); PenNormal(); /*if(wp->hilited){ ForeColor(blackColor); FrameRgn(diff); }*/ DisposeRgn(diff); #if HASGOAWAY if(wp->hilited){ GetGoAwayBox(w, &r); ForeColor(whiteColor); PaintRect(&r); } #endif } #if HASGOAWAY else if(param == wInGoAway){ GetGoAwayBox(w, &r); InvertRect(&r); } #endif SetPenState(&savePen); RGBForeColor(&save); return val; } long DoHit(CWindowPtr w, long param){ long val = 0; WindowPeek wp; Point p; Rect r; p.h = LoWord(param); p.v = HiWord(param); wp = (WindowPeek) w; if(PtInRgn(p, wp->contRgn)) val = wInContent; else if(PtInRgn(p, wp->strucRgn)){ val = wInDrag; #if HASGOAWAY GetGoAwayBox(w, &r); if(PtInRect(p, &r)) val = wInGoAway; #endif } else val = wNoHit; return val; } long DoCalcRgns(CWindowPtr w, long param){ SInt32 val = 0; WindowPeek wp; PicHandle pic; Rect r; RgnHandle rgn; wp = (WindowPeek) w; r = w->portRect; OffsetRect(&r, - ((WindowPtr)w)->portBits.bounds.left, - ((WindowPtr)w)->portBits.bounds.top); rgn = NewRgn(); pic = GetPicture(4096); PictToRgn(pic, rgn); ReleaseResource((Handle) pic); OffsetRgn(rgn, r.left, r.top); CopyRgn(rgn, wp->strucRgn); DisposeRgn(rgn); rgn = NewRgn(); pic = GetPicture(4097); PictToRgn(pic, rgn); ReleaseResource((Handle) pic); OffsetRgn(rgn, r.left, r.top); CopyRgn(rgn, wp->contRgn); DisposeRgn(rgn); return val; } void GetGoAwayBox(CWindowPtr w, Rect *r){ WindowPeek wp = (WindowPeek) w; *r = (*(wp->contRgn))->rgnBBox; r->top -= 0; r->bottom = r->top + 10; r->left += 12; r->right = r->left + 10; } // // HeartWindow.c // #define kNumFields 7 typedef struct{ Str255 str[kNumFields]; } Message; typedef Message* MessagePtr; typedef MessagePtr* MessageHandle; void DoUpdate(void); Boolean quitting; WindowPtr window; Message message; void LiveDragger(WindowPtr w, Point p, Rect *r){ Point mouse, newmouse, original, origin; EventRecord e; GrafPtr save; EventAvail(mouseDown, &e); if(!(e.modifiers & optionKey)){ DragWindow(w, p, r); } else{ GetPort(&save); SetPort(w); GetMouse(&mouse); LocalToGlobal(&mouse); SetPt(&origin, 0, 0); LocalToGlobal(&origin); original = origin; SetPort(save); while(StillDown()){ if(WaitNextEvent(updateMask, &e, 0, NULL)){ DoUpdate(); } else{ GetPort(&save); SetPort(w); GetMouse(&newmouse); LocalToGlobal(&newmouse); if(newmouse.h != mouse.h || newmouse.v != mouse.v){ if(PtInRect(newmouse, r)){ MoveWindow(w, origin.h + newmouse.h - mouse.h, origin.v + newmouse.v - mouse.v, false); SetPt(&origin,0,0); LocalToGlobal(&origin); mouse = newmouse; } else{ MoveWindow(w, original.h, original.v, false); } } SetPort(save); } } GetPort(&save); while(w != nil){ SetPort(w); InvalRect(&w->portRect); w = (WindowPtr) ((WindowPeek) w)->nextWindow; } SetPort(save); } } void GetItemHandle(DialogPtr d, short index, Handle *h){ short type; Rect r; GetDialogItem(d, index, &type, h, &r); } void EraseWindow(){ Rect r; GrafPtr savePort; GetPort(&savePort); SetPort(window); r = (*(((WindowPeek) window)->contRgn))->rgnBBox; OffsetRect(&r, -r.left, -r.top); EraseRect(&r); InvalRect(&r); SetPort(savePort); } void PStringCopy( ConstStr255Param srcString, Str255 destString ){ SInt16 index = StrLength( srcString ); do { *destString++ = *srcString++; } while ( --index >= 0 ); } void CopyMessage(MessagePtr src, MessagePtr dst){ int i; for(i = 0; i < kNumFields; i++) PStringCopy(src->str[i], dst->str[i]); } void LoadMessage(){ int i; MessageHandle mh = (MessageHandle) GetResource('hwms', 128); if(mh == nil){ for(i = 0; i < kNumFields; i++){ GetIndString(message.str[i], 128, i + 1); } } else{ CopyMessage(*mh, &message); ReleaseResource((Handle) mh); } } void EditMessage(){ short item = 0, i; Str255 str; Handle h; DialogPtr d = GetNewDialog(129, nil, (WindowPtr) -1); SetDialogDefaultItem(d, 1); SetDialogCancelItem(d, 2); SetDialogTracksCursor(d, true); for(i = 0; i < kNumFields; i++){ GetItemHandle(d, i + 3, &h); SetDialogItemText(h, message.str[i]); } ShowWindow(d); while(item != 1 && item != 2){ ModalDialog(nil, &item); } HideWindow(d); // if the user hit ok if(item == 1){ MessageHandle old; // save the info to the app's resource fork for(i = 0; i < kNumFields; i++){ GetItemHandle(d, i + 3, &h); GetDialogItemText(h, message.str[i]); } old = (MessageHandle) GetResource('hwms', 128); if(old == nil){ old = (MessageHandle) NewHandle(sizeof(Message)); if(old == nil){ StopAlert(130, nil); } else{ CopyMessage(&message, *old); AddResource((Handle) old, 'hwms', 128, "\p"); ChangedResource((Handle) old); ReleaseResource((Handle) old); } } else{ CopyMessage(&message, *old); WriteResource((Handle) old); ChangedResource((Handle) old); ReleaseResource((Handle) old); } EraseWindow(); } DisposeDialog(d); InitCursor(); } void DoAbout(){ Alert(128, nil); } void DoAppleChoice(short item){ if(item == 1) DoAbout(); else{ Str255 daName; GetMenuItemText(GetMenuHandle(128), item, daName); OpenDeskAcc(daName); } } void DoFileChoice(short item){ switch(item){ case 1: // edit the text EditMessage(); break; default: quitting = true; break; } } void DoEditChoice(short item){ } void DoMenuChoice(long choice, EventModifiers modifiers){ short id, item; id = HiWord(choice); item = LoWord(choice); switch(id){ case 128: DoAppleChoice(item); break; case 129: DoFileChoice(item); break; case 130: DoEditChoice(item); break; } HiliteMenu(0); } void DoDiskEvent(const EventRecord *e){ Point dialogCorner; if((e->message >> 16 ) != noErr){ SetPt(&dialogCorner, 112, 80); DIBadMount(dialogCorner, e->message); } } void DoOSEvent(const EventRecord *e){ SInt16 osMessage; WindowPtr w; osMessage = (e->message & osEvtMessageMask) >> 24; switch(osMessage){ case suspendResumeMessage: break; case mouseMovedMessage: break; } } void DoNullEvent(const EventRecord *e){ WindowPtr w; } void DrawCenteredString(Str255 str, short y){ MoveTo((328 - StringWidth(str))/2, y); DrawString(str); } void DoUpdate(){ GrafPtr savePort; RGBColor saveColor; Rect r; int size = 18, i, y = 116; Str255 str; BeginUpdate(window); GetForeColor(&saveColor); GetPort(&savePort); SetPort(window); ForeColor(redColor); TextFont(applFont); TextSize(size); for(i = 0; i < kNumFields; i++, y += (size + 6)){ DrawCenteredString(message.str[i], y); } SetPort(savePort); RGBForeColor(&saveColor); EndUpdate(window); } void DoMouseDown(EventRecord *e){ WindowPtr w; SInt16 part; part = FindWindow(e->where, &w); switch(part){ case inMenuBar: DoMenuChoice(MenuSelect(e->where), e->modifiers); break; case inSysWindow: SystemClick(e, w); break; case inContent: //quitting = true; break; case inDrag: //DragWindow(w, e->where, &qd.screenBits.bounds); LiveDragger(w, e->where, &qd.screenBits.bounds); break; case inGoAway: if(TrackGoAway(w, e->where)){ quitting = true; } break; } } void DoKeyDown(const EventRecord *e){ SInt16 key; key = (e->message & charCodeMask); if(e->modifiers & cmdKey){ DoMenuChoice(MenuKey(key), e->modifiers); } } void ProcessEvents(void){ EventRecord e; if(WaitNextEvent(everyEvent, &e, 0, nil)){ SetCursor(&qd.arrow); switch(e.what){ case nullEvent: DoNullEvent(&e); break; case mouseDown: DoMouseDown(&e); break; case keyDown: case autoKey: DoKeyDown(&e); break; case updateEvt: DoUpdate(); case diskEvt: DoDiskEvent(&e); break; case kHighLevelEvent: AEProcessAppleEvent(&e); break; } } DoNullEvent(&e); } pascal OSErr HandleOpenDocument(const AppleEvent *ae, AppleEvent *reply, SInt32 refCon){ return noErr; } pascal OSErr HandleOpenApplication(const AppleEvent *ae, AppleEvent *reply, SInt32 refCon){ return noErr; } pascal OSErr HandleQuitApplication(const AppleEvent *ae, AppleEvent *reply, SInt32 refCon){ quitting = true; return noErr; } OSErr InitEvents(void){ OSErr err; if((err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, NewAEEventHandlerProc(HandleOpenApplication), 0, false)) != noErr) return err; if((err = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerProc(HandleOpenDocument), 0, false)) != noErr) return err; if((err = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, NewAEEventHandlerProc(HandleOpenDocument), 0, false)) != noErr) return err; if((err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, NewAEEventHandlerProc(HandleQuitApplication), 0, false)) != noErr) return err; return noErr; } void CheckMachine(){ SysEnvRec thisMac; SysEnvirons(7, &thisMac); if(!thisMac.hasColorQD){ StopAlert(131, nil); ExitToShell(); } if(thisMac.systemVersion < 0x0700){ StopAlert(131, nil); ExitToShell(); } } OSErr Initialize(void){ OSErr err; short w1, w2, h1, h2; Handle code; MenuHandle menu; InitGraf(&qd.thePort); InitFonts(); InitWindows(); InitMenus(); TEInit(); InitDialogs(nil); InitCursor(); FlushEvents(everyEvent, 0); MaxApplZone(); MoreMasters(); MoreMasters(); MoreMasters(); CheckMachine(); err = InitEvents(); if(err != noErr) return err; LoadMessage(&message); SetMenuBar(GetNewMBar(128)); menu = GetMenu(128); AppendResMenu(menu, 'DRVR'); DrawMenuBar(); quitting = false; window = GetNewCWindow(128, nil, (WindowPtr) -1); code = GetResource('WDEF',4096); if(code != nil) ((WindowPeek) window)->windowDefProc = code; ShowWindow(window); return noErr; } void Finalize(void){ HideWindow(window); DisposeWindow(window); } void main(void){ if(Initialize() == noErr){ while(!quitting){ ProcessEvents(); } } Finalize(); }
so what did you do THIS YEAR???????? :) Don’t want you to have to sleep outside with the snow person!!! happy V day… hugs, nan
Hey I have been advertizing this as the non-technical Rob blah blog. Now you think you can just cram in a big chunk of code. Ughh
Yeah, I remember that, she was pissed. This year I got cara the most romantic gift ever… A memory chip :)
sir loves a lot, I wanted lord hugginton…oh well…
I think this could easily be ported to Carbon.
GetNewCWindow refers to a resource without for which the source would not be complete.