The Heart Shaped Window

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();
}