338 lines
8.4 KiB
C
338 lines
8.4 KiB
C
// 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.
|
|
//
|
|
// Phase 4.1 migration: menu mini-format strings, AlertTemplate, and
|
|
// NewWindowParm folded into iigs/uiBuilder.h.
|
|
|
|
#include "iigs/toolbox.h"
|
|
#include "iigs/desktop.h"
|
|
#include "iigs/eventLoop.h"
|
|
#include "iigs/uiBuilder.h"
|
|
|
|
|
|
#define apple_About 257
|
|
#define file_Quit 256
|
|
#define file_New 258
|
|
#define file_Close 255
|
|
|
|
#define wInGoAway 17
|
|
#define wInContent 19
|
|
|
|
#define mUpMask 0x0002
|
|
|
|
#define modeCopy 0
|
|
#define modeXOR 2
|
|
|
|
#define maxWindows 4
|
|
#define maxLines 50
|
|
|
|
|
|
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;
|
|
} EventRec;
|
|
|
|
|
|
typedef struct {
|
|
void *wPtr;
|
|
unsigned char *name;
|
|
unsigned short numLines;
|
|
LineRec lines[maxLines];
|
|
} WindowRecord;
|
|
|
|
|
|
// --- alphabetised forward decls -----------------------------------
|
|
static void doClose(void);
|
|
static void doNew(void);
|
|
static void drawWindow(void);
|
|
static void onAbout(uint16_t cmdId);
|
|
static void onCloseMenu(uint16_t cmdId);
|
|
static void onMenu(uint16_t menuId, uint16_t itemId);
|
|
static void onNew(uint16_t cmdId);
|
|
static void onQuit(uint16_t cmdId);
|
|
static void sketch(const IigsEventT *ev);
|
|
|
|
|
|
static void onNew(uint16_t cmdId) {
|
|
(void)cmdId;
|
|
doNew();
|
|
}
|
|
|
|
|
|
static void onCloseMenu(uint16_t cmdId) {
|
|
(void)cmdId;
|
|
doClose();
|
|
}
|
|
|
|
|
|
static const UiCmdHandlerT gCmdTable[] = {
|
|
{ apple_About, onAbout },
|
|
{ file_Quit, onQuit },
|
|
{ file_New, onNew },
|
|
{ file_Close, onCloseMenu },
|
|
};
|
|
|
|
|
|
static const UiMenuItemT gEditItems[] = {
|
|
{ 250, "Undo", 'Z', MI_CHECKED },
|
|
{ 251, "Cut", 'X', 0 },
|
|
{ 252, "Copy", 'C', 0 },
|
|
{ 253, "Paste", 'V', 0 },
|
|
{ 254, "Clear", 0, 0 },
|
|
};
|
|
|
|
static const UiMenuItemT gFileItems[] = {
|
|
{ 258, "New", 'N', 0 },
|
|
{ 255, "Close", 0, MI_CHECKED },
|
|
{ 256, "Quit", 'Q', 0 },
|
|
};
|
|
|
|
static const UiMenuItemT gAppleItems[] = {
|
|
{ 257, "About...", 0, MI_CHECKED },
|
|
};
|
|
|
|
static const UiMenuT gMenus[] = {
|
|
{ 1, "Apple", MN_APPLE, 1, gAppleItems },
|
|
{ 2, " File ", 0, 3, gFileItems },
|
|
{ 3, " Edit ", 0, 5, gEditItems },
|
|
};
|
|
|
|
|
|
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} } } }
|
|
};
|
|
|
|
|
|
// Window-content def-proc. Called by the WM with our bank set up
|
|
// (Loader sets DBR via JSL). Uses GetWRefCon to identify 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) {
|
|
unsigned short i = 0;
|
|
while (i < maxWindows && gWindows[i].wPtr != (void *)0) {
|
|
i++;
|
|
}
|
|
if (i >= maxWindows) {
|
|
return;
|
|
}
|
|
gWindows[i].numLines = 0;
|
|
|
|
// We pass a Pascal title directly via uiBuilderOpenWindow's
|
|
// contract... but uiBuilder takes a C string. Convert by skipping
|
|
// the pascal length byte and stuffing into a temporary.
|
|
char title[16];
|
|
unsigned short tn = gWindows[i].name[0];
|
|
if (tn > 14) {
|
|
tn = 14;
|
|
}
|
|
for (unsigned short k = 0; k < tn; k++) {
|
|
title[k] = (char)gWindows[i].name[k + 1];
|
|
}
|
|
title[tn] = '\0';
|
|
|
|
UiWindowT spec = {
|
|
title,
|
|
UW_STD_DOC_GZ,
|
|
{ (int16_t)(25 + i * 10), (int16_t)(10 + i * 10),
|
|
(int16_t)(180 + i * 10), (int16_t)(600 + i * 10) },
|
|
188, 615,
|
|
(uint32_t)i,
|
|
(void *)&drawWindow
|
|
};
|
|
gWindows[i].wPtr = uiBuilderOpenWindow(&spec);
|
|
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 onAbout(uint16_t cmdId) {
|
|
(void)cmdId;
|
|
uiBuilderAlert(UA_NOTE,
|
|
"Mini-CAD 1.0\r"
|
|
"Copyright 1989\r"
|
|
"Byte Works, Inc.\r\r"
|
|
"By Mike Westerfield");
|
|
}
|
|
|
|
|
|
static volatile uint16_t gDone;
|
|
|
|
|
|
static void onQuit(uint16_t cmdId) {
|
|
(void)cmdId;
|
|
gDone = 1;
|
|
iigsEventLoopQuit();
|
|
}
|
|
|
|
|
|
static void onMenu(uint16_t menuId, uint16_t itemId) {
|
|
(void)menuId;
|
|
uiBuilderDispatch(itemId, gCmdTable, (uint16_t)(sizeof gCmdTable / sizeof gCmdTable[0]));
|
|
}
|
|
|
|
|
|
static void sketch(const IigsEventT *ev) {
|
|
void *fw = FrontWindow();
|
|
if (!fw) {
|
|
return;
|
|
}
|
|
unsigned short i = (unsigned short)(unsigned long)GetWRefCon(fw);
|
|
if (i >= maxWindows) {
|
|
return;
|
|
}
|
|
if (gWindows[i].numLines >= maxLines) {
|
|
uiBuilderAlert(UA_STOP,
|
|
"The window is full -\r"
|
|
"more lines cannot be\r"
|
|
"added.");
|
|
return;
|
|
}
|
|
|
|
StartDrawing(fw);
|
|
SetSolidPenPat(15);
|
|
SetPenSize(2, 1);
|
|
SetPenMode(modeXOR);
|
|
|
|
Point firstPt;
|
|
firstPt.h = ev->whereX;
|
|
firstPt.v = ev->whereY;
|
|
GlobalToLocal(&firstPt);
|
|
MoveTo(firstPt.h, firstPt.v);
|
|
LineTo(firstPt.h, firstPt.v);
|
|
Point endPt = firstPt;
|
|
|
|
EventRec evDrag;
|
|
while (!GetNextEvent(mUpMask, &evDrag)) {
|
|
Point cur;
|
|
cur.h = evDrag.wmWhereH;
|
|
cur.v = evDrag.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);
|
|
}
|
|
}
|
|
|
|
|
|
int main(void) {
|
|
unsigned short userId = startdesk(640);
|
|
(void)userId;
|
|
|
|
paintDesktopBackdrop();
|
|
uiBuilderInstallMenuBar(gMenus, (uint16_t)(sizeof gMenus / sizeof gMenus[0]));
|
|
ShowCursor();
|
|
|
|
// Open one window so the demo has visible content immediately.
|
|
doNew();
|
|
|
|
// Use a direct TaskMaster loop so the watchdog increments on
|
|
// every iteration regardless of TaskMaster's return code.
|
|
// iigsEventLoop's onIdle only ticks on EV_NULL which TaskMaster
|
|
// rarely emits with our task mask.
|
|
IigsEventT ev;
|
|
{
|
|
unsigned char *p = (unsigned char *)&ev;
|
|
for (uint16_t i = 0; i < sizeof ev; i++) {
|
|
p[i] = 0;
|
|
}
|
|
}
|
|
ev.taskMask = 0x1FFF;
|
|
uint16_t watchdog = 0;
|
|
do {
|
|
uint16_t code = TaskMaster(0x076E, &ev);
|
|
switch (code) {
|
|
case 3: // wInMenuBar
|
|
case 25: // wInSpecial
|
|
onMenu(0, (uint16_t)ev.taskData);
|
|
break;
|
|
case wInGoAway:
|
|
doClose();
|
|
break;
|
|
case wInContent:
|
|
sketch(&ev);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
watchdog++;
|
|
} while (!gDone && watchdog < 4000);
|
|
|
|
*(volatile unsigned char *)0x70 = 0x99;
|
|
return 0;
|
|
}
|