/*---------------------------------------------------------------------------- %%File: EMTEST.C %%Unit: Event Monitor (mntr) %%Contact: daleg Event Monitor Sample Application, Main Program. The purpose of this application is to demonstrate how to process events using the Event Monitor's Rule Compiler and rule engine. ----------------------------------------------------------------------------*/ //*** genem.c -- 'generic' evtmon client-side stuff // DESCRIPTION // the client-side of an evtmon app has two parts: // - evtmon generic code (client-side) // - application-specific rules // this file is reusable (and semi-frozen) generic code. it should be // #include'ed from the app. // NOTES // WARNING: do *not* put app-specific code here. put it in the // client (emclient/libem.c). in fact in general, think twice before // modifying this file at all. //*** YY_* -- generic rules support // #define YY_BASE 1 // base features (always needed) #ifdef YY_BASE // rulc.exe doesn't create all files if a given feature isn't used, // so we can't unconditionally include these guys. plus, we don't // want the helper code if we don't need it. // turn on these features here if you use them #ifndef YY_DELAYED #define YY_DELAYED 0 // delayed actions #endif #ifndef YY_CTOR #define YY_CTOR 0 // action ctors #endif #ifndef YY_SEQCHECK #define YY_SEQCHECK 0 // seq_check #endif #define YY_OTHER 0 // not sure what emactr.h is for //#define YY_BRKPT() #endif //*** FEATURE_* -- domain-specific rules support // NOTES // untested! not sure if these #if's are done right #define FEATURE_DEMO 0 // demo code (e.g. wndproc test app) #define FEATURE_SAMPLE 0 // sample code (e.g. joe-event firer) #define FEATURE_OFFICE 0 // base office stuff #define FEATURE_TEXT 0 // text parser #define FEATURE_WORD 0 // format parser #define FEATURE_DEAD 0 // unused/dead code #define FEATURE_NYI 0 // things i don't understand #include "mso.h" #include "msoem.h" DEBUGASSERTSZ #include "msolex.h" #include "emrule.h" #include "emkwd.h" #include "emact.h" #if FEATURE_DEMO #include "emtest.h" #include "emres.h" #endif // { Files generated by Rule Compiler #include "emdef.h" // rules (#defines) #if YY_CTOR // { #include "emacts.h" // ctor macro wrappers #endif // } #define DEBUG_RULE_POINTERS // Want ptrs to nodes #include "emruli.h" // rulebase #if YY_SEQCHECK // seq_check #include "emsqck.c_" // sequences #endif // } #if FEATURE_TEXT // { // Constants #define cbEditText 1024 #ifdef STATIC_INIT #define grfLexFlagsEm (MsoGrfLexFNoReset \ | MsoGrfLexFLookup \ | MsoGrfLexFLookupIntsAndSyms \ | MsoGrfLexFAllCapsAsFormat) // EM data structures MSORULTK rgrultkEmToken[200]; // Text Token cache MSORULTK rgrultkEmFormat[100]; // Format Token cache MSOLEXS vlexs = { &vkwtbEmTOKEN_KEYTABLE, // pkwtb {rgrultkEmToken, 200, }, // rultkhToken {rgrultkEmFormat, 100, }, // rultkhFormat fTrue, // fInited isttblDefault, // isttbl grfLexFlagsEm, // grpfLexFlags _hObjectNil, // hObjectNil MsoWchLexGetNextBufferDoc, // pfnlexbuf FGetNextLexRun, // pfnlexrun FGetTokenTextObject, // pfnlextxt NULL, // pfnlexfmt NULL, // pfnlexrunDiscontig NULL, // pfnlexrunForceCompl iNil, // ichRun -1, // cchLookahead }; MSOLEXS *vplexs = &vlexs; // Global lexer state #else /* !STATIC_INIT */ MSOLEXS vlexs; // Global lexer state MSOLEXS *vplexs; // Global lexer state #endif /* STATIC_INIT */ #endif // } #if YY_BASE // { // Global variables RULS *vlpruls = &vrulsEm; #ifndef STATIC_LINK_EM RULS **_pvlprulsDLL = &vlpruls; // App's Global state #endif /* !STATIC_LINK_EM */ #endif // } #if FEATURE_DEMO // { char vszAppName[20]; // Application name HWND vhInst; // Instance HWND vhWndMain; // Main window HWND vhWndDlg; // Dialog window char vszEditText[cbEditText]; // Text of edit control int vcchEditText; // #chs in edit control static int vcchPrev = 0; // Prev edit box len static int vcpIpPrev = 0; // Prev edit box ip int vfSettingDlgItem = fFalse; // Avoid Win recursion #endif // } #if YY_BASE // { #if YY_DELAYED #include "emactr.h" // data tables MSOACTTBL vacttbl = {vrgacttrecEm}; // Global action table MSOACTTBL *_pacttbl = &vacttbl; // Glob action tbl ptr #endif #endif // } #if FEATURE_OFFICE // { EMS vems; // EM global state EMS *vpems = &vems; // Ptr to global state #endif // } #if YY_BASE // { #ifdef DEBUG #ifndef STATIC_LINK_EM int vwDebugLogFilter = fDebugFilterAll; int vwDebugLogLvl = -2; #endif // !STATIC_LINK_EM // Pointers to global debug logging vars in Mso DLL int *pvwDebugLogFilter = &vwDebugLogFilter; int *pvwDebugLogLvl = &vwDebugLogLvl; #endif /* DEBUG */ #endif // } #if YY_BASE // { #ifdef STATIC_INIT // { /* F I N I T E M */ /*---------------------------------------------------------------------------- %%Function: FInitEm %%Contact: daleg Initialize the static (compiled) Event Monitor rules. ----------------------------------------------------------------------------*/ int FInitEm(void) { #ifndef STATIC_LINK_EM /* Mirror global debug pointers in Mso97.dll */ Debug(pvwDebugLogLvl = MsoPwDebugLogLvl(&pvwDebugLogFilter);) #endif /* !STATIC_LINK_EM */ Debug(*pvwDebugLogLvl = 7); #ifndef STATIC_LINK_EM /* Mirror global pointers (vlpruls, etc) in Mso97.dll */ _pvlprulsDLL = MsoPvlprulsMirror(&vlpruls); *_pvlprulsDLL = &vrulsEm; #endif /* !STATIC_LINK_EM */ #ifdef DYN_RULES /* Load dynamic rules */ FLoadDynEmRules(); #endif /* DYN_RULES */ #if FEATURE_OFFICE /* For performance reasons, action table is global */ vacttbl.prultkh = &vplexs->rultkhToken; #endif #if FEATURE_DEMO /* Allow Event Drivers to run */ EnableEM(); #endif /* Propagate the values through the rule network */ MsoScheduleIrul(irul_YYSTD_INIT, fTrue); #ifdef DYN_RULES MsoScheduleIrul(irul_YYSTD_LOADING_RULEBASE, fTrue); #endif /* DYN_RULES */ MsoEvaluateEvents(rulevtEm_YYSTD); return fTrue; } #else /* }{ !STATIC_INIT */ /* F I N I T E M */ /*---------------------------------------------------------------------------- %%Function: FInitEm %%Contact: daleg Initialize the static (compiled) Event Monitor rules. ----------------------------------------------------------------------------*/ int FInitEm(void) { #ifndef STATIC_LINK_EM /* Mirror global debug pointers in Mso97.dll */ Debug(pvwDebugLogLvl = MsoPwDebugLogLvl(&pvwDebugLogFilter);) #endif /* !STATIC_LINK_EM */ Debug(*pvwDebugLogLvl = 7); /* Initialize rule base, for performance reasons, rulebase is global */ if (!MsoFInitStaticRuls(&vrulsEm, &vrulsEm)) return fFalse; #ifndef STATIC_LINK_EM /* Mirror global pointers (vlpruls, etc) in Mso97.dll */ _pvlprulsDLL = MsoPvlprulsMirror(&vlpruls); *_pvlprulsDLL = &vrulsEm; #endif /* !STATIC_LINK_EM */ #ifdef DYN_RULES /* Load dynamic rules */ FLoadDynEmRules(); #endif /* DYN_RULES */ #if FEATURE_OFFICE /* Initialize the lexer to scan the doc */ if (!(vplexs = MsoPlexsLexInitDoc (&vlexs, _hObjectNil, FGetNextLexRun, FGetTokenTextObject, NULL, NULL, 200, 100))) return fFalse; vplexs->pkwtb = &vkwtbEmTOKEN_KEYTABLE; /* For performance reasons, action table is global */ vacttbl.prultkh = &vplexs->rultkhToken; #endif #if FEATURE_DEMO /* Allow Event Drivers to run */ EnableEM(); #endif /* Propagate the values through the rule network */ MsoScheduleIrul(irul_YYSTD_INIT, fTrue); #ifdef DYN_RULES MsoScheduleIrul(irul_YYSTD_LOADING_RULEBASE, fTrue); #endif /* DYN_RULES */ MsoEvaluateEvents(rulevtEm_YYSTD); return fTrue; } #endif /* } STATIC_INIT */ #endif // } #if YY_BASE // { /* F E V A L E M R U L E */ /*---------------------------------------------------------------------------- %%Function: FEvalEmRule %%Contact: daleg Evaluate the rule associated with the given rule ID number. Return a boolean value for whether its value was TRUE or FALSE. ----------------------------------------------------------------------------*/ #include "emeval.c" #endif // } #if FEATURE_DEMO // { /* W I N M A I N */ /*---------------------------------------------------------------------------- %%Function: WinMain %%Contact: daleg Main routine. ----------------------------------------------------------------------------*/ int CALLBACK WinMain( HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow ) { MSG msg; int nRc; char szString[256]; GdiSetBatchLimit(1); strcpy(vszAppName, "EMTEST"); vhInst = hInstance; if (!hPrevInstance) { /* Register window classes if first instance of application */ if ((nRc = nCwRegisterClasses()) == -1) { /* Put up msg if registering one of the windows failed */ LoadString(vhInst, IDS_ERR_REGISTER_CLASS, szString, sizeof(szString)); MessageBox(NULL, szString, NULL, MB_ICONEXCLAMATION); return nRc; } } /* Create application's Main window */ vhWndMain = CreateWindow(vszAppName, NULL, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN | WS_OVERLAPPED, 0, 0, 400, 400, NULL, NULL, vhInst, NULL); /* If could not create main window, be nice before quitting */ if (vhWndMain == NULL) { LoadString(vhInst, IDS_ERR_CREATE_WINDOW, szString, sizeof(szString)); MessageBox(NULL, szString, NULL, MB_ICONEXCLAMATION); return IDS_ERR_CREATE_WINDOW; } /* Initialize the rulebase */ if (!FInitEm()) return 1; /* Display main window */ ShowWindow(vhWndMain, nCmdShow); /* Until WM_QUIT message */ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } /* Do clean up before exiting from the application */ CwUnRegisterClasses(); return msg.wParam; } /* W N D P R O C */ /*---------------------------------------------------------------------------- %%Function: WndProc %%Contact: daleg Windows proc for main window. ----------------------------------------------------------------------------*/ LONG CALLBACK WndProc( HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam ) { HMENU hMenu = 0; int nRc = 0; switch (Message) { case WM_COMMAND: FEmEvalDialog(wParam); switch (LOWORD(wParam)) { case IDM_DIALOG: /* Respond to the menu item named "Dialog" */ { FARPROC lpfnDIALOGSMsgProc; lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC) DIALOGSMsgProc, vhInst); nRc = DialogBox(vhInst, MAKEINTRESOURCE(IDM_DIALOG), hWnd, lpfnDIALOGSMsgProc); FreeProcInstance(lpfnDIALOGSMsgProc); } break; default: return DefWindowProc(hWnd, Message, wParam, lParam); } break; case WM_CLOSE: /* Destroy child windows, modeless dialogs, then, this window */ DestroyWindow(hWnd); /* Quit the application */ if (hWnd == vhWndMain) PostQuitMessage(0); break; default: return DefWindowProc(hWnd, Message, wParam, lParam); } return 0L; } /* D I A L O G S M S G P R O C */ /*---------------------------------------------------------------------------- %%Function: DIALOGSMsgProc %%Contact: daleg Dialog proc for inner window. ----------------------------------------------------------------------------*/ BOOL CALLBACK DIALOGSMsgProc( HWND hWndDlg, UINT Message, WPARAM wParam, LPARAM lParam ) { switch (Message) { case WM_INITDIALOG: cwCenter(hWndDlg, 0); vcchPrev = 0; vcpIpPrev = 0; vhWndDlg = hWndDlg; break; case WM_CLOSE: break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_EDIT1: switch (HIWORD(wParam)) { case EN_CHANGE: vcchEditText = GetDlgItemText(hWndDlg, IDC_EDIT1, vszEditText, cbEditText); if (!vfSettingDlgItem) { MSOCP cpIp; XCHAR wch; /* Get IP (Insertion Point) position */ SendDlgItemMessage(hWndDlg, IDC_EDIT1, EM_GETSEL, 0, (LPARAM)&cpIp); /* Get character typed */ wch = (vcchEditText > vcchPrev ? vszEditText[cpIp - 1] // Char typed : xchBackspace); // Backspace /* Notify Event Monitor CHAR driver */ FEmEvalChar(wch, cpIp, vszEditText, vcchEditText); /* Track prev text positions for invalidation detect */ vcchPrev = vcchEditText; vcpIpPrev = cpIp; } break; } break; case IDC_BUTTON1: case IDC_BUTTON2: case IDC_START: case IDC_STOP: default: FEmEvalDialog(wParam); break; case IDOK: FEmEvalDialog(wParam); EndDialog(hWndDlg, FALSE); break; } break; default: return FALSE; } return TRUE; } #endif // } #if FEATURE_SAMPLE // { joe event firer /* F E M E V A L D I A L O G */ /*---------------------------------------------------------------------------- %%Function: FEmEvalDialog %%Contact: daleg Event Monitor Event Driver for DIALOG events. ----------------------------------------------------------------------------*/ int FEmEvalDialog(WPARAM wParam) { short idc = LOWORD(wParam); IRUL irul; #if FEATURE_DEMO /* Make sure we are enabled (e.g. macros not running) */ if (FEmDisabled()) return fFalse; #endif /* Match the (sparse) dialog object to its associated (contig) event, if any */ vlpruls->irulPrimaryEvent = irul = (IRUL) MsoPkwdlhLookupL(idc, &vkwtbEmIDC_KEYTABLE)->tk; debugEM1(2, "DIALOG EVENT: %-20.20s\n", LpchRulName(LprulFromIrul(irul))); /* Push event into appropriate queue (resets depth) */ MsoScheduleIrul(irul, fTrue); MsoScheduleIrul(irulIDC_, idc); // non-mapped event (autoclear global) /* Propagate the values through the rule network */ MsoEvaluateEvents(rulevtEmDIALOG); // n.b. IDC_ is now autocleared /* Evaluate any pending actions */ if (_pacttbl->pactPending) DoPendingActions(); } /* F E M E V A L C H A R */ /*---------------------------------------------------------------------------- %%Function: FEmEvalChar %%Contact: daleg Event Driver for CHAR events in Event Monitor. Evaluate CHAR (KEYBOARD) events within the Event Monitor rulebase. Return whether or not it was handled. ----------------------------------------------------------------------------*/ int FEmEvalChar( XCHAR wch, MSOCP cpIp, XCHAR *wz, int ichMac ) { IRUL irul; #if FEATURE_DEMO /* Make sure we are enabled (e.g. macros not running) */ if (FEmDisabled()) return fFalse; #endif /* Primitive invalidation */ if (FCheckIfMustReset(wch, ichMac, cpIp)) InvalLex(vplexs); /* If our running state is still valid, adjust run variables */ if (FInvalLex(vplexs)) FResetEm((vplexs->cpIp = cpIp) - 1, wz, ichMac); /* Create a "lookahead" token to hold chars not yet scanned by lexer */ vplexs->cchLookahead++; _CacheTkTextNext(vplexs); /* Look up character in keyword table */ irul = (IRUL) MsoPkwdLookupName(&wch, 1, &vkwtbEmCHAR_KEYTABLE)->tk; debugEM3(2, "CHAR EVENT: %-20.20s for '%s' (0x%x)\n", LpchRulName(LprulFromIrul(irul)), MsoSzFromRgxchDebug(&wch, 1), wch); /* Push text token into appropriate queue (resets depth) */ MsoScheduleIrul(irulCH_, wch); MsoScheduleIrul(vlpruls->irulPrimaryEvent = irul, fTrue); /* Propagate the values through the rule network */ MsoEvaluateEvents(rulevtEmCHAR); /* Call TOKEN event driver to process any token events */ FEmEvalToken(cpIp, wz, ichMac); } /* E M E V A L T K I R U L */ /*---------------------------------------------------------------------------- %%Function: EmEvalTkIrul %%Contact: daleg Evaluate a TOKEN event irul. ----------------------------------------------------------------------------*/ void EmEvalTkIrul(IRUL irul) { MSORULTK *prultk; debugEM2(2, "TOKEN EVENT: %-20.20s \"%.100s\"\n", LpchIrulName(irul), MsoSzLexTokenText(vplexs)); /* Push pending token events into appropriate queues */ prultk = PrultkFromTokenIrultk(vplexs, vplexs->rultkhToken.irultkMin); while (vplexs->rultkhToken.irultkMin != vplexs->rultkhToken.irultkLim) { #ifdef DEBUG if (prultk->tk != irul) { debugEM2(4, "EXTRA TOKEN EVENT: %-20.20s %d\n", LpchIrulName(prultk->tk), prultk->lValue); debugEM1(8, " at CP %ld\n", prultk->cpFirst); } #endif /* DEBUG */ /* Push text token into appropriate queue (resets depth) */ MsoSignalEventIrul(IrulFromTk(prultk->tk), prultk->lValue); /* Move to next cache record, wrapping around if necessary */ IncrTokenPrultk(vplexs, &prultk, &vplexs->rultkhToken.irultkMin); } /* Push any applicable format tokens into appropriate queues */ if (vplexs->rultkhFormat.irultkMin != vplexs->rultkhFormat.irultkLim) PushIrultkFormatPending(); /* Push text token into appropriate queue (resets depth) */ MsoScheduleIrul(irulTOKEN_, vlpruls->irulPrimaryEvent = irul); /* Propagate the values through the rule network */ MsoEvaluateEvents(rulevtEmTOKEN); } #endif // } #if FEATURE_SAMPLE // { /* F E M E V A L T O K E N */ /*---------------------------------------------------------------------------- %%Function: FEmEvalToken %%Contact: daleg Event Monitor Event Driver for TOKEN events. ----------------------------------------------------------------------------*/ int FEmEvalToken( MSOCP cpIp, XCHAR *wz, int ichMac ) { IRUL irul; #if FEATURE_DEMO /* Make sure we are enabled (e.g. macros not running) */ if (FEmDisabled()) return fFalse; #endif /* If our running state is still valid, adjust run variables */ if (!FInvalLex(vplexs)) { long dwz = wz - vplexs->pxchBuffer; vplexs->pxchTkStart += dwz; vplexs->pxchNext += dwz; vplexs->pxchRun += dwz; vplexs->pxchBuffer += dwz; vplexs->cpIp = cpIp; vplexs->cpLim = ichMac; vplexs->cchRemain = vplexs->pxchBuffer + vplexs->cpLim - vplexs->pxchNext; } /* Else rescan back up to IP */ else if (!FResetEm((vplexs->cpIp = cpIp) - 1, wz, ichMac)) return fFalse; /* Inject and eval any complete tokens up to IP */ while (FValidIrul(irul = IrulFromTk(MsoTkLexTextCpLim(vplexs, cpIp)))) EmEvalTkIrul(irul); /* Evaluate any pending actions */ if (_pacttbl->pactPending) DoPendingActions(); /* All typed characters must now have been seen by the lexer */ if (vplexs->cchLookahead > 0) vplexs->cchLookahead = 0; } #endif // } #if FEATURE_TEXT // { /* P U S H I R U L T K F O R M A T P E N D I N G */ /*---------------------------------------------------------------------------- %%Function: PushIrultkFormatPending %%Contact: daleg Push any pending format tokens into appropriate queues. ----------------------------------------------------------------------------*/ void PushIrultkFormatPending(void) { MSORULTK *prultk; debugEM1(8, "Checking for formatting before CP %ld\n", CpLexTokenFirst(vplexs)); prultk = PrultkFormatFromIrultk(vplexs, vplexs->rultkhFormat.irultkMin); while (vplexs->rultkhFormat.irultkMin != vplexs->rultkhFormat.irultkLim && (prultk->cpFirst < CpLexTokenFirst(vplexs) + DcpLexToken(vplexs) || (DcpLexToken(vplexs) == 0 && prultk->cpFirst <= CpLexTokenFirst(vplexs)))) { debugEM2(2, "FORMAT : %-20.20s %ld\n", LpchIrulName(prultk->tk), prultk->lValue); debugEM1(6, " at CP %ld\n", prultk->cpFirst); /* Push format token into appropriate queue (resets depth) */ MsoSignalEventIrul(IrulFromTk(prultk->tk), prultk->lValue); /* Move to next cache record, wrapping around if necessary */ IncrFormatPrultk(vplexs, &prultk, &vplexs->rultkhFormat.irultkMin); } } #endif // } #if FEATURE_TEXT // { /* F R E S E T E M */ /*---------------------------------------------------------------------------- %%Function: FResetEm %%Contact: daleg Reset the Event Monitor rules and lexer due to an IP (cursor) change, or due to some form of invalidation. ----------------------------------------------------------------------------*/ int FResetEm( MSOCP cpIp, XCHAR *wz, int ichMac ) { MSOCP cpObject = 0; IRUL irul; /* Initialize lexer to point to start of buffer */ vplexs->pObjectIp = PObjectCur(); vplexs->pxchBuffer = vplexs->pxchBufferIp = vplexs->pxchRun = wz; MsoLexSetPos(vplexs, cpObject, 0); debugEM0(0, "====================================================\n"); debugEM3(0, " FResetEm: RESETTING: pObjectIp %x cpLine %ld cp %ld\n", vplexs->pObjectIp, cpObject, cpIp); debugEM0(0, "====================================================\n"); /* Reset rule base TOKEN state variables */ MsoClearEventsForRulevts(rulevtEmTOKEN, drulevtToken, (vpems->fInternalReset ? rultPersistentRule | rultAlwaysPersist : rultAlwaysPersist), fTrue, fFalse); vplexs->wInterval = CIntervalsRulevt(rulevtEmTOKEN); /* Reset lexer base state */ MsoResetLexState(vplexs, fTrue/*fFullReset*/); vplexs->cpLim = 0; vplexs->fInvalLexer = fFalse; /* Mark start of scan */ MsoCacheTkText(vplexs, irulSTART, fTrue); MsoScheduleIrul(irulSTART, fTrue); /* Mark start of token cache, but not as events */ MsoCacheTkText(vplexs, irulEND_OBJ, fTrue); vplexs->rultkhToken.irultkMin = vplexs->rultkhToken.irultkLim; /* Run only rules not marked as INTERACTIVE_ONLY */ SetCurrRulg(rulgEmALWAYS); /* Inject and eval any complete tokens up to IP */ while (FValidIrul(irul = IrulFromTk(MsoTkLexTextCpLim(vplexs, cpIp)))) EmEvalTkIrul(irul); /* Run only rules not marked as INTERACTIVE_ONLY */ SetCurrRulg(rulgEmINTERACTIVE_ONLY); /* Mark that we are synchronized */ vplexs->cchLookahead = 0; return fTrue; } /* F G E T T O K E N T E X T O B J E C T */ /*---------------------------------------------------------------------------- FGetTokenTextObject %%Contact: smueller Determine whether the text of the requested token needs to be fetched from the document. If so, do so, leaving the results in ppxch and pcch, and return fTrue. Otherwise, return fFalse. ----------------------------------------------------------------------------*/ int WIN_CALLBACK FGetTokenTextObject( MSORULTK *prultk, const XCHAR **ppxch, // RETURN int *pcch, // RETURN struct _MSOLEXS *plexs ) { if (plexs->pObject != NULL) { // NOT SUPPORTED YET return fTrue; } return fFalse; } /* F G E T N E X T L E X R U N */ /*---------------------------------------------------------------------------- %%Function: FGetNextLexRun %%Contact: daleg Return next run of text within the current object , and set the lexer run-state variables. For this demo, there ain't any, but we will show a commented-out sample. ----------------------------------------------------------------------------*/ int WIN_CALLBACK FGetNextLexRun(MSOCP cpLim, MSOLEXS *plexs) { int fStatus = fTrue; const XCHAR *pxchPrevEnd = plexs->pxchNext; /* If prev run at end of current object, move to next object */ if (FLexEndOfScan(vplexs)) { /* If still have a pending token, complete it first */ if (DcpLexCurr(vplexs) > 0) return fFalse; /* Create exactly one inter-object "created" tkEND_OBJ character */ if (plexs->fCreateEndObjCh) { /* Make this run "created" text, i.e. no dcp */ fStatus = fFalse; plexs->cpRun += plexs->ccpRun; plexs->ichRun += plexs->cchRun; plexs->cchRun = 1; plexs->ccpRun = 0; #ifdef READY /* Mark run as "created", so dcp calcs will work properly */ plexs->fAdjustTokenCps = fTrue; plexs->dcpCreated += 1; if (plexs->cpFirstCreated == 0L) plexs->cpFirstCreated = plexs->cpRun; #endif /* READY */ } /* Else get next object's text */ else { if (!FGetNextLexObject(cpLim, plexs)) return fFalse; /* If in object of IP has passed upper lim of scan, block lexer */ if (plexs->pObject == plexs->pObjectIp && cpLim != msocpMax && cpLim <= 0 /* plexs->cpRun */) fStatus = fFalse; } } /* Else move to next run */ else { plexs->cpRun += plexs->ccpRun; plexs->ichRun += plexs->cchRun; #ifdef PORT_THIS plexs->cchRun = vcchEditText - plexs->ichRun; #endif /* PORT_THIS */ plexs->ccpRun = plexs->cchRun; // Reset buffer pointer if run in a different buffer #ifdef PORT_THIS plexs->pxchBuffer = vszEditText; #endif /* PORT_THIS */ } /* Set Run length and pointer */ plexs->cchRemain = plexs->cchRun; plexs->pxchNext = (plexs->pxchRun = plexs->pxchBuffer + plexs->ichRun); AssertSz0(pxchPrevEnd == plexs->pxchNext || pxchPrevEnd == plexs->pxchTkStart, "Discontiguous runs!"); /* If run starts new text (line), reset non-cached tk start pointer */ if (pxchPrevEnd == plexs->pxchTkStart) plexs->pxchTkStart = plexs->pxchNext; return fStatus; } /* F G E T N E X T L E X O B J E C T */ /*---------------------------------------------------------------------------- %%Function: FGetNextLexObject %%Contact: daleg Set the "run" state variables to the first run of a new object, including the text buffer, and the run lengths. ----------------------------------------------------------------------------*/ int WIN_CALLBACK FGetNextLexObject(MSOCP cpLim, MSOLEXS *plexs) { // If this is the first time thru, do initialization stuff if (plexs->ichRun == iNil) { // YOUR INITS HERE } // Fetch new object text plexs->pObject = PObjectCur(); // Fetch new text (in our case, it is globally static) plexs->pxchBuffer = vszEditText; plexs->cchRun = plexs->cpLim = vcchEditText; plexs->pxchRun = plexs->pxchBuffer; // Set up other state variables plexs->ichRun = 0; plexs->cpRun = 0; plexs->cpObject = 0; plexs->ccpRun = plexs->cchRun; SetCpLexTokenFirst(vplexs, SetCpLexTokenNext(vplexs, 0)); return fTrue; } /* F L E X F O R C E C O M P L E T E */ /*---------------------------------------------------------------------------- %%Function: FLexForceComplete %%Contact: daleg Force the current token to complete within the lexer. This is a callback function that gets called when we wish to cause the lexer to finish a token without peeking at the next character. This is generally when we are moving the IP out of a cell in a table, and wish to perform automatic actions anyway. Once we have completed the token, we clear the callback flag, to resume normal operation. ----------------------------------------------------------------------------*/ int WIN_CALLBACK FLexForceComplete(MSOCP cpLim, MSOLEXS *plexs) { XCHAR wch; /* If in the middle of a token complete it if next is EOO or delim */ // REVIEW daleg: This should check for more than just spaces after if (plexs->iuState > 0 && (cpLim == vplexs->cpLim || (wch = *vplexs->pxchNext) == xchSpace)) return fTrue; /* Do not call this routine next time */ plexs->pfnlexrunForceComplete = NULL; /* Force an END_OBJ token (event) if we are at the cell boundary */ return (cpLim == vplexs->cpLim); } #endif // } #if YY_BASE // { /* P A C T P C A */ /*---------------------------------------------------------------------------- %%Function: PactPca %%Contact: daleg Create a Delayed-Action record, using a MSOCA to define the edit range. ----------------------------------------------------------------------------*/ MSOACT *PactPca( MSOACTTBL *pacttbl, int actt, MSOCA *pca, ... ) { va_list ap; MSOACT *pact; /* Safety first: make sure we have an action structure */ if (!pacttbl) return NULL; /* Start varargs */ va_start(ap, pca); /* Create a new Delayed Action record, and push arg list into it */ pact = MsoPactAp(pacttbl, actt, (sizeof(MSOCA) + sizeof(long) - 1)/sizeof(long), ap); /* End varargs */ va_end(ap); /* Calculate starting CP */ pact->rec1_ca.ca = *pca; /* Insert the MSOACT record into the edit queue in CP sorted order */ MsoInsertPact(pact, &pacttbl->pactPending); return pact; } #if YY_DELAYED // { long WIN_CALLBACK DcpDoAct( MSOACT *pact, MSOACTTBL *pacttbl, long *pdcp, MSOCA *pca, MSOACT **ppactNext, int *pfDiscard ); /* D O P E N D I N G A C T I O N S */ /*---------------------------------------------------------------------------- %%Function: DoPendingActions %%Contact: daleg Execute pending actions in delay action queue. ----------------------------------------------------------------------------*/ void DoPendingActions(void) { _pacttbl->cpFirstEditPrev = -1; _pacttbl->dcpEditPrev = 0; _pacttbl->cpLimEdit = 0; MsoReversePact(&_pacttbl->pactPending); MsoDcpDoActs(&_pacttbl->pactPending, _pacttbl, 0, fTrue, -1, DcpDoAct); } /* D C P D O A C T */ /*---------------------------------------------------------------------------- %%Function: DcpDoAct %%Contact: daleg Execute the action given by the MSOACT record. ----------------------------------------------------------------------------*/ long WIN_CALLBACK DcpDoAct( MSOACT *pact, MSOACTTBL *pacttbl, long *pdcp, MSOCA *pca, MSOACT **ppactNext, int *pfDiscard ) { switch (pact->rec1.actt) { #include "emact.c_" } return 0; } #endif // } #endif // } #if FEATURE_DEMO // { /* D C P R E P L A C E T E X T */ /*---------------------------------------------------------------------------- %%Function: DcpReplaceText %%Contact: daleg Replace the text range given by the range of chars with the new string. ----------------------------------------------------------------------------*/ int DcpReplaceText( void *pObject, int cpFirst, int cpLim, char *sz ) { int cch = CchSz(sz); int dcp = cch - (cpLim - cpFirst); int cchOrig; char rgch[cbEditText]; MSOCP cpIp; /* Get current edit control value */ cchOrig = GetDlgItemText(vhWndDlg, IDC_EDIT1, rgch, cbEditText); /* Do not attempt to expand beyond control's capacity. */ if (cchOrig + dcp >= cbEditText) return 0; /* Replace the string */ CopyRgb(rgch + cpLim, rgch + cpLim + dcp, cchOrig - cpLim + 1); CopyRgbNo(sz, rgch + cpFirst, cch); /* Set the edit control value with the new string, preserving the IP */ vfSettingDlgItem = fTrue; // Prevent recursion SendDlgItemMessage(vhWndDlg, IDC_EDIT1, EM_GETSEL, 0, (LPARAM)&cpIp); SendDlgItemMessage(vhWndDlg, IDC_EDIT1, WM_SETTEXT, 0, (LPARAM)&rgch[0]); SendDlgItemMessage(vhWndDlg, IDC_EDIT1, EM_SETSEL, cpIp + dcp, cpIp + dcp); vcchEditText += dcp; vfSettingDlgItem = fFalse; debugEM0(2, "Replaced text\n"); return dcp; } #endif // } #if FEATURE_TEXT // { /* F C H E C K I F M U S T R E S E T */ /*---------------------------------------------------------------------------- %%Function: FCheckIfMustReset %%Contact: daleg Do a primitive check to see if we should invalidate our running rulebase state, and do a full reset scan. THIS IS NOT A CANONICAL ROUTINE, but there should be something equivalent in each app. ----------------------------------------------------------------------------*/ int FCheckIfMustReset(XCHAR wch, int ichMac, MSOCP cpIp) { if (wch == xchBackspace) return fTrue; /* If user typed a new character, emit character event */ /* REVIEW: pasting will result in multiple new characters, of which we only notice the last */ if (ichMac > vcchPrev) { Assert (cpIp > 0); /* Without notification of IP moves, there is no way to accurately identify when lexer needs invalidating; for safety, we could do so on every character. For demonstrating the lexer, we'll do so only when the IP didn't move forward by exactly one character, which is a decent approximation of the right time. */ /* REVIEW: find a more pleasant way to deal with this, like subclassing the edit control to get precise control over notifications. */ if (cpIp != vcpIpPrev + 1) return fTrue; } return fFalse; } #endif // } #if FEATURE_DEMO // { /* N C W R E G I S T E R C L A S S E S */ /*---------------------------------------------------------------------------- %%Function: nCwRegisterClasses %%Contact: daleg Register window classes ----------------------------------------------------------------------------*/ int nCwRegisterClasses(void) { WNDCLASS wndclass; memset(&wndclass, 0x00, sizeof(WNDCLASS)); /* load WNDCLASS with window's characteristics */ wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW; wndclass.lpfnWndProc = WndProc; /* Extra storage for Class and Window objects */ wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = vhInst; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); /* Create brush for erasing background */ wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wndclass.lpszMenuName = vszAppName; /* Menu Name is App Name */ wndclass.lpszClassName = vszAppName; /* Class Name is App Name */ if (!RegisterClass(&wndclass)) return -1; return (0); } /* C W C E N T E R */ /*---------------------------------------------------------------------------- %%Function: cwCenter %%Contact: daleg Center the main window. ----------------------------------------------------------------------------*/ void cwCenter(hWnd, top) HWND hWnd; int top; { POINT pt; RECT swp; RECT rParent; int iwidth; int iheight; /* get the rectangles for the parent and the child */ GetWindowRect(hWnd, &swp); GetClientRect(vhWndMain, &rParent); /* calculate the height and width for MoveWindow */ iwidth = swp.right - swp.left; iheight = swp.bottom - swp.top; /* find the center point and convert to screen coordinates */ pt.x = (rParent.right - rParent.left) / 2; pt.y = (rParent.bottom - rParent.top) / 2; ClientToScreen(vhWndMain, &pt); /* calculate the new x, y starting point */ pt.x = pt.x - (iwidth / 2); pt.y = pt.y - (iheight / 2); /* top will adjust the window position, up or down */ if (top) pt.y = pt.y + top; /* move the window */ MoveWindow(hWnd, pt.x, pt.y, iwidth, iheight, FALSE); } /* C W U N R E G I S T E R C L A S S E S */ /*---------------------------------------------------------------------------- %%Function: CwUnRegisterClasses %%Contact: daleg Un-register the windows classes. ----------------------------------------------------------------------------*/ void CwUnRegisterClasses(void) { WNDCLASS wndclass; memset(&wndclass, 0x00, sizeof(WNDCLASS)); UnregisterClass(vszAppName, vhInst); } #endif // } #if FEATURE_DEAD // { /* A S S E R T L S Z P R O C */ /*---------------------------------------------------------------------------- %%Function: AssertLszProc %%Contact: daleg Print assertion message, and prompt for whether to (f)ail, or (i)gnore. ----------------------------------------------------------------------------*/ int AssertLszProc( const char *szExtra, const char *szFile, int line ) { Fail("ASSERTION FAILED: %s IN %s line %d\n", szExtra, szFile, line); return 1; } /* F A I L */ /*---------------------------------------------------------------------------- %%Function: Fail %%Contact: daleg Emit a failure message and exit. ----------------------------------------------------------------------------*/ void __cdecl Fail(const char *sz, ...) { va_list ap; char szBuf[256]; /* Start variable arglist */ va_start(ap, sz); wvsprintf(szBuf, sz, ap); OutputDebugStringA("FATAL ERROR: "); OutputDebugStringA(szBuf); OutputDebugStringA("\n"); /* End variable arglist */ va_end(ap); // _asm int 3; exit(1); } #endif // } #if FEATURE_DEAD // { /* F N E N C L P C H */ /*---------------------------------------------------------------------------- %%Function: FNeNcLpch %%Contact: daleg Compare two strings, case insensitive. ----------------------------------------------------------------------------*/ BOOL FNeNcLpch( register const uchar *pch1, register const uchar *pch2, register int cch ) { while (cch-- > 0) { if (ChUpper(*pch1++) != ChUpper(*pch2++)) return fTrue; } return fFalse; } #endif // }