#pragma nda NDAOpen NDAClose NDAAction NDAInit 15 0xffff "--Dictionary\\H**" #pragma lint - 1 #pragma optimize - 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "connection.h" #include "nda.h" #define TBBlack "\x01" "C" "\x00\x00" #define TBBlue "\x01" "C" "\x11\x11" #define TBRed "\x01" "C" "\x44\x44" unsigned NDAStartUpTools(Word memID, StartStopRecord *ssRef); void NDAShutDownTools(StartStopRecord *ssRef); typedef struct NDAResourceCookie { Word oldPrefs; Word oldRApp; Word resFileID; } NDAResourceCookie; void NDAResourceRestore(NDAResourceCookie *cookie); void NDAResourceShutDown(NDAResourceCookie *cookie); Word NDAResourceStartUp(Word memID, Word access, NDAResourceCookie *cookie); Word MyID; Word FlagTCP; Word ToolsLoaded; GrafPortPtr MyWindow; Handle TECtrlHandle; Handle TextHandle; LongWord TextHandleSize; LongWord TextHandleUsed; static StartStopRecord ss = {0, 0, 0, 0, 4, { 0x12, 0x0000, /* QD Aux */ 0x1b, 0x0000, /* Font Manager */ 0x22, 0x0000, /* Text Edit */ 0x36, 0x0300, /* TCP */ } }; static NDAResourceCookie resInfo; static Connection connection; enum { st_none, st_idle, st_connect, st_login, st_client, st_define1, st_define2, st_define3, st_quit, st_force_quit, st_disconnect, }; static unsigned st = 0; static LongWord qtick = 0; const char *ReqName = "\pTCP/IP~kelvin~dict~"; int define(Word ipid, const char *dict); pascal void MarinettiCallback(char *str); void EnableControls(void); void DisableControls(void); void AppendText(word length, char *cp) { Handle h = TextHandle; LongWord size; size = TextHandleUsed + length; if (size > TextHandleSize) { size += 4095; size &= ~4095; if (h) { HUnlock(h); SetHandleSize(size, h); if (_toolErr) return; HLock(h); TextHandleSize = size; } else { TextHandle = h = NewHandle(size, MyID, attrLocked|attrNoSpec, 0); if (_toolErr) return; TextHandleSize = size; } } BlockMove(cp, *h + TextHandleUsed, length); TextHandleUsed += length; } void AppendText2(word length, char *cp) { unsigned i; unsigned char c; unsigned start = 0; for (i = 0; i < length; ++i) { c = cp[i]; if (c == '{' || c == '}' || c == 0x01) { /* flush any pending data */ if (start < i) AppendText(i - start, cp + start); if (c == '{') AppendText(4, TBBlue); if (c == '}') AppendText(4, TBBlack); start = i + 1; } } if (start < length) AppendText(length - start, cp + start); } void SetText(void) { longword oldStart, oldEnd; TESetSelection((Pointer)-1, (Pointer)-1, TECtrlHandle); TESetText(teDataIsTextBox2|teTextIsPtr, (Ref)*TextHandle, TextHandleUsed, 0, NULL, TECtrlHandle); TextHandleUsed = 0; } static char buffer[512]; void TCPLoop(void) { static srBuff sr; static rlrBuff rlr; Word terr; unsigned x; int status; Word ipid = connection.ipid; switch (st) { case st_idle: if (GetTick() < qtick) break; case st_force_quit: TCPIPWriteTCP(ipid, "QUIT\r\n", 6, 1, 0); qtick = GetTick() + 60 * 30; st = st_quit; return; break; case st_connect: x = ConnectionPoll(&connection); switch (connection.state) { case kConnectionStateConnected: MarinettiCallback("\pConnected"); ++st; break; case kConnectionStateDisconnected: MarinettiCallback("\pDisconnected"); EnableControls(); st = st_none; break; case kConnectionStateError: MarinettiCallback("\pConnection Error"); EnableControls(); st = st_none; break; } return; case st_disconnect: x = ConnectionPoll(&connection); switch (connection.state) { case kConnectionStateDisconnected: case kConnectionStateError: MarinettiCallback("\pDisconnected"); EnableControls(); st = st_none; break; } return; redo: case st_login: case st_client: case st_define1: case st_define2: case st_define3: case st_quit: TCPIPPoll(); sr.srRcvQueued = 0; terr = TCPIPStatusTCP(ipid, &sr); if (sr.srRcvQueued == 0) { if (GetTick() >= qtick) { /* timeout */ MarinettiCallback("\pTimed out"); ConnectionClose(&connection); st = st_disconnect; } return; } /* all data should be \r\n delimited. we want to keep the \r */ terr = TCPIPReadLineTCP(ipid, "\p\n", 0x0000, (Ref)&buffer, sizeof(buffer) - 2, &rlr); if (terr == tcperrConClosing) terr = 0; if (terr) return; if (!rlr.rlrIsDataFlag) return; qtick += 60 * 15; /* bump timeout */ /* ensure a trailing \r and 0 */ x = rlr.rlrBuffCount; if (x) { --x; if (buffer[x] != '\r') { buffer[x] = '\r'; ++x; } ++x; } buffer[x] = 0; rlr.rlrBuffCount = x; if (st != st_define3) { unsigned i; status = 0; for (i = 0;; ++i) { unsigned c = buffer[i]; if (isdigit(c)) { status *= 10; status += c - '0'; continue; } if (isspace(c) || c == 0) break; status = -1; break; } if (i == 0) status = -1; } break; } switch (st) { case st_login: /* expect a 220 status */ if (status == 220) { ++st; terr = TCPIPWriteTCP(ipid, "CLIENT dict-nda-iigs\r\n", 22, 1, 0); } else { MarinettiCallback("\pLogin Error"); st = st_force_quit; } break; case st_client: /* expect 250 status */ if (status == 250) { ++st; /* send define string... */ define(ipid, NULL); } else { MarinettiCallback("\pClient Error"); AppendText(4, TBRed); AppendText(rlr.rlrBuffCount, buffer); SetText(); st = st_force_quit; } break; case st_define1: /* expect 550, 552, or 150 status */ if (status == 150) { ++st; MarinettiCallback("\pReceiving..."); break; } if (status == 552) { MarinettiCallback("\pNo match"); st = st_idle; } else { MarinettiCallback("\pServer Error"); AppendText(4, TBRed); AppendText(rlr.rlrBuffCount, buffer); SetText(); st = st_idle; } EnableControls(); qtick = GetTick() + 60 * 60 * 2; /* 2-minute timeout */ break; case st_define2: /* expect 151 */ if (status == 151) { ++st; } else if (status == 250) { st = st_idle; qtick = GetTick() + 60 * 60 * 2; /* 2-minute timeout */ SetText(); EnableControls(); MarinettiCallback("\pConnected"); } else { MarinettiCallback("\pServer Error"); AppendText(4, TBRed); AppendText(rlr.rlrBuffCount, buffer); SetText(); st = st_idle; } break; case st_define3: /* expect definition text. '.' terminates. */ if (buffer[0] == '.') { AppendText(rlr.rlrBuffCount - 1, buffer + 1); if (buffer[1] == '\r') { --st; } } else { AppendText2(rlr.rlrBuffCount, buffer); } goto redo; break; case st_quit: /* expect 221 but doesn't really matter... */ ConnectionClose(&connection); st = st_disconnect; MarinettiCallback("\pDisconnecting..."); break; } } // activate/inactivate controls based on Marinetti status void UpdateStatus(Boolean redraw) { if (FlagTCP) { SetInfoRefCon((LongWord) "\pNetwork Connected", MyWindow); } else { SetInfoRefCon((LongWord) "\pNetwork Disconnected", MyWindow); } if (redraw) DrawInfoBar(MyWindow); } #pragma databank 1 /* * Watch for TCP status updates. */ pascal word HandleRequest(word request, longword dataIn, longword dataOut) { Word oldRApp; oldRApp = GetCurResourceApp(); SetCurResourceApp(MyID); if (request == TCPIPSaysNetworkUp) { FlagTCP = true; UpdateStatus(true); } if (request == TCPIPSaysNetworkDown) { FlagTCP = false; UpdateStatus(true); } SetCurResourceApp(oldRApp); } pascal void MarinettiCallback(char *str) { if (MyWindow) { SetInfoRefCon((LongWord)str, MyWindow); DrawInfoBar(MyWindow); } } pascal void DrawInfo(Rect *rect, const char *str, GrafPortPtr w) { SetForeColor(0x00); SetBackColor(0x0f); EraseRect(rect); if (str) { MoveTo(/*8, 22*/ rect->h1 + 10, rect->v1 + 9); DrawString(str); } } void DrawWindow(void) { DrawControls(GetPort()); } #pragma databank 0 void MakeCtlTargetByID(GrafPortPtr window, Long resID) { CtlRecHndl h = GetCtlHandleFromID(window, resID); if (h) MakeThisCtlTarget(h); } void NDAInit(Word code) { if (code) { MyWindow = NULL; FlagTCP = false; ToolsLoaded = false; MyID = MMStartUp(); st = st_none; } else { if (ToolsLoaded) NDAShutDownTools(&ss); ToolsLoaded = false; } } void NDAClose(void) { // if running, shut down. AcceptRequests(ReqName, MyID, NULL); ConnectionAbort(&connection); st = st_none; CloseWindow(MyWindow); MyWindow = NULL; TECtrlHandle = NULL; NDAResourceShutDown(&resInfo); if (TextHandle) { DisposeHandle(TextHandle); TextHandle = NULL; TextHandleUsed = 0; TextHandleSize = 0; } } GrafPortPtr NDAOpen(void) { MyWindow = NULL; TextHandle = NULL; TextHandleSize = 0; TextHandleSize = 0; st = st_none; if (!ToolsLoaded) { if (NDAStartUpTools(MyID, &ss)) { NDAShutDownTools(&ss); return NULL; } ToolsLoaded = true; } if (TCPIPLongVersion() < 0x03006011) { AlertWindow(awCString, NULL, (Ref) "24~Marinetti 3.0b11 or newer required.~^Ok"); return NULL; } // Check if Marinetti Active. FlagTCP = TCPIPGetConnectStatus(); if (NDAResourceStartUp(MyID, readEnable, &resInfo)) { MyWindow = NewWindow2(NULL, 0, DrawWindow, NULL, refIsResource, rWindow, rWindParam1); SetInfoDraw(DrawInfo, MyWindow); UpdateStatus(0); AcceptRequests(ReqName, MyID, &HandleRequest); MakeCtlTargetByID(MyWindow, rCtrlLE); SetSysWindow(MyWindow); ShowWindow(MyWindow); SelectWindow(MyWindow); TECtrlHandle = (Handle)GetCtlHandleFromID(MyWindow, rCtrlTE); ConnectionInit(&connection, MyID, MarinettiCallback); NDAResourceRestore(&resInfo); return MyWindow; } return NULL; } static char word_to_define[256]; int define(Word ipid, const char *dict) { word terr; int ok; unsigned x; static char buffer[512]; if (!dict || !*dict) dict = "!"; x = sprintf(buffer, "DEFINE %s \"%b\"\r\n", dict, word_to_define); terr = TCPIPWriteTCP(ipid, buffer, x, 1, 0); return 0; } void DoDefine(void) { unsigned i; Handle handle; handle = (Handle)GetCtlHandleFromID(MyWindow, rCtrlTE); TESetText(teDataIsTextBlock|teTextIsPtr, (Ref)"", 0, 0, NULL, handle); GetLETextByID(MyWindow, rCtrlLE, (StringPtr)word_to_define); i = word_to_define[0]; while (i && isspace(word_to_define[i])) --i; if (!i) return; word_to_define[0] = i; word_to_define[i+1] = 0; /* considerations: 1. is the network connected? 2. is a tcp connection already established? */ if (!FlagTCP) { MarinettiCallback("\pConnecting to network..."); TCPIPConnect(MarinettiCallback); if (!FlagTCP) return; } qtick = GetTick() + 30 * 60; DisableControls(); switch(st) { case st_idle: define(connection.ipid, NULL); st = st_define1; break; default: ConnectionAbort(&connection); case st_none: ConnectionOpenC(&connection, "dict.org", 2628); st = st_connect; break; } } void EnableControls(void) { HiliteCtlByID(noHilite, MyWindow, rCtrlLE); HiliteCtlByID(noHilite, MyWindow, rCtrlDefine); } void DisableControls(void) { HiliteCtlByID(inactiveHilite, MyWindow, rCtrlLE); HiliteCtlByID(inactiveHilite, MyWindow, rCtrlDefine); } void EditKeys(Word key, Handle target, LongWord targetID) { LERecHndl le; if (!target) { target = (Handle)FindTargetCtl(); if (!target) return; targetID = GetCtlID((CtlRecHndl)target); } le = (LERecHndl)(*(CtlRecHndl)target)->ctlData; switch (key) { case 'a': case 'A': if (targetID == rCtrlTE) { LongWord start = 0; LongWord end = 0; TEGetTextInfo((Pointer)&end, 1, TECtrlHandle); TESetSelection((Pointer)start, (Pointer)end, TECtrlHandle); } else if (targetID == rCtrlLE) { Word len = LEGetTextLen(le); LESetSelect(0, len, le); } break; case 'c': case 'C': if (targetID == rCtrlTE) { TECopy(target); } if (targetID == rCtrlLE) { LECopy(le); } break; case 'v': case 'V': if (targetID == rCtrlLE) LEPaste(le); break; case 'x': case 'X': if (targetID == rCtrlLE) LECut(le); break; case 'k': if (targetID == rCtrlLE) LEDelete(le); break; } } word NDAAction(void *param, int code) { word eventCode; static EventRecord event = {0}; Handle target = NULL; if (code == runAction) { if (st) TCPLoop(); return 1; } else if (code == eventAction) { BlockMove((Pointer)param, (Pointer)&event, 16); event.wmTaskMask = 0x001FFFFF; eventCode = TaskMasterDA(0, &event); switch (eventCode) { case updateEvt: BeginUpdate(MyWindow); DrawWindow(); EndUpdate(MyWindow); break; case wInControl: target = (Handle)event.wmTaskData2; switch ((Word)event.wmTaskData4) { case rCtrlDefine: DoDefine(); break; case rCtrlTE: case rCtrlLE: /* check for control-C, control-A */ if (event.what != keyDownEvt) break; if (!(event.modifiers & 0x0100)) break; EditKeys(event.message, (Handle)event.wmTaskData2, event.wmTaskData4); break; } break; case keyDownEvt: /* line-edit needs to be handled here */ if (!(event.modifiers & 0x0100)) break; EditKeys(event.message, 0, 0); break; } } else if (code == copyAction) { EditKeys('c', 0, 0); return 1; // yes we handled it. } else if (code == pasteAction) { EditKeys('v', 0, 0); return 1; // yes we handled it. } else if (code == cutAction) { EditKeys('x', 0, 0); return 1; // yes we handled it. } else if (code == clearAction) { EditKeys('k', 0, 0); } return 0; }