// minicad.c - faithful port of ORCA-C's MiniCAD.cc sample. // // Mike Westerfield, Byte Works 1989. Original at // tools/orca-c/C.Samples/Desktop.Samples/MiniCAD.cc. // // A simple multi-window CAD: File>New opens a drawing window (up to // 4), click+drag inside a window's content rubber-bands a line, // release commits it. File>Close closes the front window. Each // window's lines are remembered so the WM can repaint on update. #include "iigs/toolbox.h" #include "iigs/desktop.h" #define apple_About 257 #define file_Quit 256 #define file_New 258 #define file_Close 255 #define wInMenuBar 3 #define wInSpecial 25 #define wInGoAway 17 #define wInContent 19 #define mUpMask 0x0002 #define modeCopy 0 #define modeXOR 2 #define topMost ((void *)-1L) #define bottomMost ((void *)0) #define maxWindows 4 #define maxLines 50 #define norml 0 #define stop 1 #define note 2 #define caution 3 #define buttonItem 10 #define statText 136 #define itemDisable 0x8000 typedef struct { short v1, h1, v2, h2; } Rect; typedef struct { short v, h; } Point; typedef struct { Point p1, p2; } LineRec; typedef struct { unsigned short wmWhat; unsigned long wmMessage; unsigned long wmWhen; short wmWhereV, wmWhereH; unsigned short wmModifiers; unsigned long wmTaskData; unsigned long wmTaskMask; unsigned long wmLastClickTick; unsigned long wmClickCount; unsigned long wmTaskData2; unsigned long wmTaskData3; unsigned long wmTaskData4; } WmTaskRec; typedef struct { unsigned short wmWhat; unsigned long wmMessage; unsigned long wmWhen; short wmWhereV, wmWhereH; unsigned short wmModifiers; } EventRec; typedef struct { unsigned short paramLength; unsigned short wFrameBits; void *wTitle; unsigned long wRefCon; Rect wZoom; void *wColor; short wYOrigin, wXOrigin; short wDataH, wDataV; short wMaxHeight, wMaxWidth; short wScrollVer, wScrollHor; short wPageVer, wPageHor; unsigned long wInfoRefCon; short wInfoHeight; void *wFrameDefProc; void *wInfoDefProc; void *wContDefProc; Rect wPosition; void *wPlane; void *wStorage; } NewWindowParm; typedef struct { short itemID; short itemRectV1, itemRectH1, itemRectV2, itemRectH2; unsigned short itemType; void *itemDescr; short itemValue; short itemFlag; void *itemColor; } ItemTemplate; typedef struct { short atRectV1, atRectH1, atRectV2, atRectH2; short atBtnHorz; short atBeep0, atBeep1, atBeep2, atBeep3; void *atSound; void *atResv1; void *atResv2; void *atItemList[8]; } AlertTemplate; typedef struct { void *wPtr; unsigned char *name; unsigned short numLines; LineRec lines[maxLines]; } WindowRecord; static unsigned char editMenuStr[] = ">> Edit \\N3\r" "--Undo\\N250V*Zz\r" "--Cut\\N251*Xx\r" "--Copy\\N252*Cc\r" "--Paste\\N253*Vv\r" "--Clear\\N254\r" ".\r"; static unsigned char fileMenuStr[] = ">> File \\N2\r" "--New\\N258*Nn\r" "--Close\\N255V\r" "--Quit\\N256*Qq\r" ".\r"; static unsigned char appleMenuStr[] = ">>@\\XN1\r" "--About...\\N257V\r" ".\r"; static unsigned char gAboutMsg[] = "\x3d" "Mini-CAD 1.0\r" "Copyright 1989\r" "Byte Works, Inc.\r\r" "By Mike Westerfield"; static unsigned char gTitle0[] = "\x07Paint 1"; static unsigned char gTitle1[] = "\x07Paint 2"; static unsigned char gTitle2[] = "\x07Paint 3"; static unsigned char gTitle3[] = "\x07Paint 4"; static WindowRecord gWindows[maxWindows] = { { (void *)0, gTitle0, 0, { { {0,0}, {0,0} } } }, { (void *)0, gTitle1, 0, { { {0,0}, {0,0} } } }, { (void *)0, gTitle2, 0, { { {0,0}, {0,0} } } }, { (void *)0, gTitle3, 0, { { {0,0}, {0,0} } } } }; static WmTaskRec gEvent; static volatile unsigned short gDone; static void doAlert(unsigned short kind, void *msg) { static unsigned char okStr[] = "\x02OK"; static ItemTemplate button = { 1, 36, 15, 0, 0, buttonItem, okStr, 0, 0, (void *)0 }; static ItemTemplate message = { 100, 5, 100, 90, 280, itemDisable | statText, (void *)0, 0, 0, (void *)0 }; static AlertTemplate alertRec = { 50, 180, 107, 460, 2, 0x80, 0x80, 0x80, 0x80, (void *)0, (void *)0, (void *)0, { (void *)0, (void *)0, (void *)0, (void *)0, (void *)0, (void *)0, (void *)0, (void *)0 } }; SetForeColor(0); SetBackColor(15); message.itemDescr = msg; alertRec.atItemList[0] = (void *)&button; alertRec.atItemList[1] = (void *)&message; alertRec.atItemList[2] = (void *)0; switch (kind) { case norml: (void)Alert(&alertRec, (void *)0); break; case stop: (void)StopAlert(&alertRec, (void *)0); break; case note: (void)NoteAlert(&alertRec, (void *)0); break; case caution: (void)CautionAlert(&alertRec, (void *)0); break; default: break; } } // Window-content def-proc. The WM calls this with DBR set to our // bank (Loader sets up the JSL chain). We use GetWRefCon on the // current port to know which gWindows[] entry to redraw. static void drawWindow(void) { unsigned long refcon = (unsigned long)GetWRefCon(GetPort()); unsigned short i = (unsigned short)refcon; if (i >= maxWindows) return; WindowRecord *wp = &gWindows[i]; if (wp->numLines == 0) return; SetPenMode(modeCopy); SetSolidPenPat(0); SetPenSize(2, 1); for (unsigned short j = 0; j < wp->numLines; j++) { LineRec *lp = &wp->lines[j]; MoveTo(lp->p1.h, lp->p1.v); LineTo(lp->p2.h, lp->p2.v); } } static void doNew(void) { static NewWindowParm wp; unsigned short i = 0; while (i < maxWindows && gWindows[i].wPtr != (void *)0) i++; if (i >= maxWindows) return; gWindows[i].numLines = 0; unsigned char *p = (unsigned char *)℘ for (unsigned short k = 0; k < sizeof wp; k++) p[k] = 0; wp.paramLength = (unsigned short)sizeof wp; wp.wFrameBits = 0x4007 | 0x0020 | 0x0080 | 0x0400 | 0x4000; // fTitle+fClose+fVis+fMove+fGrow wp.wTitle = gWindows[i].name; wp.wRefCon = (unsigned long)i; wp.wMaxHeight = 188; wp.wMaxWidth = 615; wp.wPosition.v1 = (short)(25 + i * 10); wp.wPosition.h1 = (short)(10 + i * 10); wp.wPosition.v2 = (short)(180 + i * 10); wp.wPosition.h2 = (short)(600 + i * 10); wp.wContDefProc = (void *)&drawWindow; wp.wPlane = topMost; gWindows[i].wPtr = NewWindow(&wp); if (i == maxWindows - 1) { DisableMItem(file_New); } } static void doClose(void) { void *fw = FrontWindow(); if (!fw) return; unsigned short i = (unsigned short)(unsigned long)GetWRefCon(fw); if (i >= maxWindows) return; CloseWindow(gWindows[i].wPtr); gWindows[i].wPtr = (void *)0; EnableMItem(file_New); } static void menuAbout(void) { doAlert(note, gAboutMsg); } static void sketch(void) { void *fw = FrontWindow(); if (!fw) return; unsigned short i = (unsigned short)(unsigned long)GetWRefCon(fw); if (i >= maxWindows) return; if (gWindows[i].numLines >= maxLines) { static unsigned char fullMsg[] = "\x3a" "The window is full -\r" "more lines cannot be\r" "added."; doAlert(stop, fullMsg); return; } StartDrawing(fw); SetSolidPenPat(15); SetPenSize(2, 1); SetPenMode(modeXOR); Point firstPt; firstPt.h = gEvent.wmWhereH; firstPt.v = gEvent.wmWhereV; GlobalToLocal(&firstPt); MoveTo(firstPt.h, firstPt.v); LineTo(firstPt.h, firstPt.v); Point endPt = firstPt; EventRec ev; while (!GetNextEvent(mUpMask, &ev)) { Point cur; cur.h = ev.wmWhereH; cur.v = ev.wmWhereV; GlobalToLocal(&cur); if (cur.h != endPt.h || cur.v != endPt.v) { MoveTo(firstPt.h, firstPt.v); LineTo(endPt.h, endPt.v); MoveTo(firstPt.h, firstPt.v); LineTo(cur.h, cur.v); endPt = cur; } } // Erase final XOR line. MoveTo(firstPt.h, firstPt.v); LineTo(endPt.h, endPt.v); if (firstPt.h != endPt.h || firstPt.v != endPt.v) { unsigned short n = gWindows[i].numLines++; gWindows[i].lines[n].p1 = firstPt; gWindows[i].lines[n].p2 = endPt; SetPenMode(modeCopy); SetSolidPenPat(0); MoveTo(firstPt.h, firstPt.v); LineTo(endPt.h, endPt.v); } } static void handleMenu(unsigned short menuNum) { switch (menuNum) { case apple_About: menuAbout(); break; case file_Quit: gDone = 1; break; case file_New: doNew(); break; case file_Close: doClose(); break; default: break; } HiliteMenu(0, (unsigned short)(gEvent.wmTaskData >> 16)); } static void initMenus(void) { InsertMenu(NewMenu(editMenuStr), 0); InsertMenu(NewMenu(fileMenuStr), 0); InsertMenu(NewMenu(appleMenuStr), 0); FixAppleMenu(1); FixMenuBar(); DrawMenuBar(); } int main(void) { unsigned short userId = startdesk(640); (void)userId; paintDesktopBackdrop(); initMenus(); gEvent.wmTaskMask = 0x1FFFL; ShowCursor(); // Open one window so the demo has visible content immediately. doNew(); gDone = 0; unsigned short watchdog = 0; do { unsigned short event = TaskMaster(0x076E, &gEvent); switch (event) { case wInSpecial: case wInMenuBar: handleMenu((unsigned short)gEvent.wmTaskData); break; case wInGoAway: doClose(); break; case wInContent: sketch(); break; default: break; } watchdog++; } while (!gDone && watchdog < 4000); *(volatile unsigned char *)0x70 = 0x99; return 0; }