Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1373 lines
41 KiB

  1. /*----------------------------------------------------------------------------
  2. %%File: EMTEST.C
  3. %%Unit: Event Monitor (mntr)
  4. %%Contact: daleg
  5. Event Monitor Sample Application, Main Program.
  6. The purpose of this application is to demonstrate how to process events
  7. using the Event Monitor's Rule Compiler and rule engine.
  8. ----------------------------------------------------------------------------*/
  9. //*** genem.c -- 'generic' evtmon client-side stuff
  10. // DESCRIPTION
  11. // the client-side of an evtmon app has two parts:
  12. // - evtmon generic code (client-side)
  13. // - application-specific rules
  14. // this file is reusable (and semi-frozen) generic code. it should be
  15. // #include'ed from the app.
  16. // NOTES
  17. // WARNING: do *not* put app-specific code here. put it in the
  18. // client (emclient/libem.c). in fact in general, think twice before
  19. // modifying this file at all.
  20. //*** YY_* -- generic rules support
  21. //
  22. #define YY_BASE 1 // base features (always needed)
  23. #ifdef YY_BASE
  24. // rulc.exe doesn't create all files if a given feature isn't used,
  25. // so we can't unconditionally include these guys. plus, we don't
  26. // want the helper code if we don't need it.
  27. // turn on these features here if you use them
  28. #ifndef YY_DELAYED
  29. #define YY_DELAYED 0 // delayed actions
  30. #endif
  31. #ifndef YY_CTOR
  32. #define YY_CTOR 0 // action ctors
  33. #endif
  34. #ifndef YY_SEQCHECK
  35. #define YY_SEQCHECK 0 // seq_check
  36. #endif
  37. #define YY_OTHER 0 // not sure what emactr.h is for
  38. //#define YY_BRKPT()
  39. #endif
  40. //*** FEATURE_* -- domain-specific rules support
  41. // NOTES
  42. // untested! not sure if these #if's are done right
  43. #define FEATURE_DEMO 0 // demo code (e.g. wndproc test app)
  44. #define FEATURE_SAMPLE 0 // sample code (e.g. joe-event firer)
  45. #define FEATURE_OFFICE 0 // base office stuff
  46. #define FEATURE_TEXT 0 // text parser
  47. #define FEATURE_WORD 0 // format parser
  48. #define FEATURE_DEAD 0 // unused/dead code
  49. #define FEATURE_NYI 0 // things i don't understand
  50. #include "mso.h"
  51. #include "msoem.h"
  52. DEBUGASSERTSZ
  53. #include "msolex.h"
  54. #include "emrule.h"
  55. #include "emkwd.h"
  56. #include "emact.h"
  57. #if FEATURE_DEMO
  58. #include "emtest.h"
  59. #include "emres.h"
  60. #endif
  61. // { Files generated by Rule Compiler
  62. #include "emdef.h" // rules (#defines)
  63. #if YY_CTOR // {
  64. #include "emacts.h" // ctor macro wrappers
  65. #endif // }
  66. #define DEBUG_RULE_POINTERS // Want ptrs to nodes
  67. #include "emruli.h" // rulebase
  68. #if YY_SEQCHECK // seq_check
  69. #include "emsqck.c_" // sequences
  70. #endif
  71. // }
  72. #if FEATURE_TEXT // {
  73. // Constants
  74. #define cbEditText 1024
  75. #ifdef STATIC_INIT
  76. #define grfLexFlagsEm (MsoGrfLexFNoReset \
  77. | MsoGrfLexFLookup \
  78. | MsoGrfLexFLookupIntsAndSyms \
  79. | MsoGrfLexFAllCapsAsFormat)
  80. // EM data structures
  81. MSORULTK rgrultkEmToken[200]; // Text Token cache
  82. MSORULTK rgrultkEmFormat[100]; // Format Token cache
  83. MSOLEXS vlexs =
  84. {
  85. &vkwtbEmTOKEN_KEYTABLE, // pkwtb
  86. {rgrultkEmToken, 200, }, // rultkhToken
  87. {rgrultkEmFormat, 100, }, // rultkhFormat
  88. fTrue, // fInited
  89. isttblDefault, // isttbl
  90. grfLexFlagsEm, // grpfLexFlags
  91. _hObjectNil, // hObjectNil
  92. MsoWchLexGetNextBufferDoc, // pfnlexbuf
  93. FGetNextLexRun, // pfnlexrun
  94. FGetTokenTextObject, // pfnlextxt
  95. NULL, // pfnlexfmt
  96. NULL, // pfnlexrunDiscontig
  97. NULL, // pfnlexrunForceCompl
  98. iNil, // ichRun
  99. -1, // cchLookahead
  100. };
  101. MSOLEXS *vplexs = &vlexs; // Global lexer state
  102. #else /* !STATIC_INIT */
  103. MSOLEXS vlexs; // Global lexer state
  104. MSOLEXS *vplexs; // Global lexer state
  105. #endif /* STATIC_INIT */
  106. #endif // }
  107. #if YY_BASE // {
  108. // Global variables
  109. RULS *vlpruls = &vrulsEm;
  110. #ifndef STATIC_LINK_EM
  111. RULS **_pvlprulsDLL = &vlpruls; // App's Global state
  112. #endif /* !STATIC_LINK_EM */
  113. #endif // }
  114. #if FEATURE_DEMO // {
  115. char vszAppName[20]; // Application name
  116. HWND vhInst; // Instance
  117. HWND vhWndMain; // Main window
  118. HWND vhWndDlg; // Dialog window
  119. char vszEditText[cbEditText]; // Text of edit control
  120. int vcchEditText; // #chs in edit control
  121. static int vcchPrev = 0; // Prev edit box len
  122. static int vcpIpPrev = 0; // Prev edit box ip
  123. int vfSettingDlgItem = fFalse; // Avoid Win recursion
  124. #endif // }
  125. #if YY_BASE // {
  126. #if YY_DELAYED
  127. #include "emactr.h" // data tables
  128. MSOACTTBL vacttbl = {vrgacttrecEm}; // Global action table
  129. MSOACTTBL *_pacttbl = &vacttbl; // Glob action tbl ptr
  130. #endif
  131. #endif // }
  132. #if FEATURE_OFFICE // {
  133. EMS vems; // EM global state
  134. EMS *vpems = &vems; // Ptr to global state
  135. #endif // }
  136. #if YY_BASE // {
  137. #ifdef DEBUG
  138. #ifndef STATIC_LINK_EM
  139. int vwDebugLogFilter = fDebugFilterAll;
  140. int vwDebugLogLvl = -2;
  141. #endif // !STATIC_LINK_EM
  142. // Pointers to global debug logging vars in Mso DLL
  143. int *pvwDebugLogFilter = &vwDebugLogFilter;
  144. int *pvwDebugLogLvl = &vwDebugLogLvl;
  145. #endif /* DEBUG */
  146. #endif // }
  147. #if YY_BASE // {
  148. #ifdef STATIC_INIT // {
  149. /* F I N I T E M */
  150. /*----------------------------------------------------------------------------
  151. %%Function: FInitEm
  152. %%Contact: daleg
  153. Initialize the static (compiled) Event Monitor rules.
  154. ----------------------------------------------------------------------------*/
  155. int FInitEm(void)
  156. {
  157. #ifndef STATIC_LINK_EM
  158. /* Mirror global debug pointers in Mso97.dll */
  159. Debug(pvwDebugLogLvl = MsoPwDebugLogLvl(&pvwDebugLogFilter);)
  160. #endif /* !STATIC_LINK_EM */
  161. Debug(*pvwDebugLogLvl = 7);
  162. #ifndef STATIC_LINK_EM
  163. /* Mirror global pointers (vlpruls, etc) in Mso97.dll */
  164. _pvlprulsDLL = MsoPvlprulsMirror(&vlpruls);
  165. *_pvlprulsDLL = &vrulsEm;
  166. #endif /* !STATIC_LINK_EM */
  167. #ifdef DYN_RULES
  168. /* Load dynamic rules */
  169. FLoadDynEmRules();
  170. #endif /* DYN_RULES */
  171. #if FEATURE_OFFICE
  172. /* For performance reasons, action table is global */
  173. vacttbl.prultkh = &vplexs->rultkhToken;
  174. #endif
  175. #if FEATURE_DEMO
  176. /* Allow Event Drivers to run */
  177. EnableEM();
  178. #endif
  179. /* Propagate the values through the rule network */
  180. MsoScheduleIrul(irul_YYSTD_INIT, fTrue);
  181. #ifdef DYN_RULES
  182. MsoScheduleIrul(irul_YYSTD_LOADING_RULEBASE, fTrue);
  183. #endif /* DYN_RULES */
  184. MsoEvaluateEvents(rulevtEm_YYSTD);
  185. return fTrue;
  186. }
  187. #else /* }{ !STATIC_INIT */
  188. /* F I N I T E M */
  189. /*----------------------------------------------------------------------------
  190. %%Function: FInitEm
  191. %%Contact: daleg
  192. Initialize the static (compiled) Event Monitor rules.
  193. ----------------------------------------------------------------------------*/
  194. int FInitEm(void)
  195. {
  196. #ifndef STATIC_LINK_EM
  197. /* Mirror global debug pointers in Mso97.dll */
  198. Debug(pvwDebugLogLvl = MsoPwDebugLogLvl(&pvwDebugLogFilter);)
  199. #endif /* !STATIC_LINK_EM */
  200. Debug(*pvwDebugLogLvl = 7);
  201. /* Initialize rule base, for performance reasons, rulebase is global */
  202. if (!MsoFInitStaticRuls(&vrulsEm, &vrulsEm))
  203. return fFalse;
  204. #ifndef STATIC_LINK_EM
  205. /* Mirror global pointers (vlpruls, etc) in Mso97.dll */
  206. _pvlprulsDLL = MsoPvlprulsMirror(&vlpruls);
  207. *_pvlprulsDLL = &vrulsEm;
  208. #endif /* !STATIC_LINK_EM */
  209. #ifdef DYN_RULES
  210. /* Load dynamic rules */
  211. FLoadDynEmRules();
  212. #endif /* DYN_RULES */
  213. #if FEATURE_OFFICE
  214. /* Initialize the lexer to scan the doc */
  215. if (!(vplexs = MsoPlexsLexInitDoc
  216. (&vlexs, _hObjectNil, FGetNextLexRun,
  217. FGetTokenTextObject, NULL, NULL, 200, 100)))
  218. return fFalse;
  219. vplexs->pkwtb = &vkwtbEmTOKEN_KEYTABLE;
  220. /* For performance reasons, action table is global */
  221. vacttbl.prultkh = &vplexs->rultkhToken;
  222. #endif
  223. #if FEATURE_DEMO
  224. /* Allow Event Drivers to run */
  225. EnableEM();
  226. #endif
  227. /* Propagate the values through the rule network */
  228. MsoScheduleIrul(irul_YYSTD_INIT, fTrue);
  229. #ifdef DYN_RULES
  230. MsoScheduleIrul(irul_YYSTD_LOADING_RULEBASE, fTrue);
  231. #endif /* DYN_RULES */
  232. MsoEvaluateEvents(rulevtEm_YYSTD);
  233. return fTrue;
  234. }
  235. #endif /* } STATIC_INIT */
  236. #endif // }
  237. #if YY_BASE // {
  238. /* F E V A L E M R U L E */
  239. /*----------------------------------------------------------------------------
  240. %%Function: FEvalEmRule
  241. %%Contact: daleg
  242. Evaluate the rule associated with the given rule ID number.
  243. Return a boolean value for whether its value was TRUE or FALSE.
  244. ----------------------------------------------------------------------------*/
  245. #include "emeval.c"
  246. #endif // }
  247. #if FEATURE_DEMO // {
  248. /* W I N M A I N */
  249. /*----------------------------------------------------------------------------
  250. %%Function: WinMain
  251. %%Contact: daleg
  252. Main routine.
  253. ----------------------------------------------------------------------------*/
  254. int CALLBACK WinMain(
  255. HANDLE hInstance,
  256. HANDLE hPrevInstance,
  257. LPSTR lpszCmdLine,
  258. int nCmdShow
  259. )
  260. {
  261. MSG msg;
  262. int nRc;
  263. char szString[256];
  264. GdiSetBatchLimit(1);
  265. strcpy(vszAppName, "EMTEST");
  266. vhInst = hInstance;
  267. if (!hPrevInstance)
  268. {
  269. /* Register window classes if first instance of application */
  270. if ((nRc = nCwRegisterClasses()) == -1)
  271. {
  272. /* Put up msg if registering one of the windows failed */
  273. LoadString(vhInst, IDS_ERR_REGISTER_CLASS, szString,
  274. sizeof(szString));
  275. MessageBox(NULL, szString, NULL, MB_ICONEXCLAMATION);
  276. return nRc;
  277. }
  278. }
  279. /* Create application's Main window */
  280. vhWndMain = CreateWindow(vszAppName,
  281. NULL,
  282. WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
  283. | WS_MAXIMIZEBOX | WS_THICKFRAME
  284. | WS_CLIPCHILDREN | WS_OVERLAPPED,
  285. 0, 0, 400, 400,
  286. NULL, NULL, vhInst, NULL);
  287. /* If could not create main window, be nice before quitting */
  288. if (vhWndMain == NULL)
  289. {
  290. LoadString(vhInst, IDS_ERR_CREATE_WINDOW, szString, sizeof(szString));
  291. MessageBox(NULL, szString, NULL, MB_ICONEXCLAMATION);
  292. return IDS_ERR_CREATE_WINDOW;
  293. }
  294. /* Initialize the rulebase */
  295. if (!FInitEm())
  296. return 1;
  297. /* Display main window */
  298. ShowWindow(vhWndMain, nCmdShow);
  299. /* Until WM_QUIT message */
  300. while (GetMessage(&msg, NULL, 0, 0))
  301. {
  302. TranslateMessage(&msg);
  303. DispatchMessage(&msg);
  304. }
  305. /* Do clean up before exiting from the application */
  306. CwUnRegisterClasses();
  307. return msg.wParam;
  308. }
  309. /* W N D P R O C */
  310. /*----------------------------------------------------------------------------
  311. %%Function: WndProc
  312. %%Contact: daleg
  313. Windows proc for main window.
  314. ----------------------------------------------------------------------------*/
  315. LONG CALLBACK WndProc(
  316. HWND hWnd,
  317. UINT Message,
  318. WPARAM wParam,
  319. LPARAM lParam
  320. )
  321. {
  322. HMENU hMenu = 0;
  323. int nRc = 0;
  324. switch (Message)
  325. {
  326. case WM_COMMAND:
  327. FEmEvalDialog(wParam);
  328. switch (LOWORD(wParam))
  329. {
  330. case IDM_DIALOG:
  331. /* Respond to the menu item named "Dialog" */
  332. {
  333. FARPROC lpfnDIALOGSMsgProc;
  334. lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC) DIALOGSMsgProc,
  335. vhInst);
  336. nRc = DialogBox(vhInst, MAKEINTRESOURCE(IDM_DIALOG), hWnd,
  337. lpfnDIALOGSMsgProc);
  338. FreeProcInstance(lpfnDIALOGSMsgProc);
  339. }
  340. break;
  341. default:
  342. return DefWindowProc(hWnd, Message, wParam, lParam);
  343. }
  344. break;
  345. case WM_CLOSE:
  346. /* Destroy child windows, modeless dialogs, then, this window */
  347. DestroyWindow(hWnd);
  348. /* Quit the application */
  349. if (hWnd == vhWndMain)
  350. PostQuitMessage(0);
  351. break;
  352. default:
  353. return DefWindowProc(hWnd, Message, wParam, lParam);
  354. }
  355. return 0L;
  356. }
  357. /* D I A L O G S M S G P R O C */
  358. /*----------------------------------------------------------------------------
  359. %%Function: DIALOGSMsgProc
  360. %%Contact: daleg
  361. Dialog proc for inner window.
  362. ----------------------------------------------------------------------------*/
  363. BOOL CALLBACK DIALOGSMsgProc(
  364. HWND hWndDlg,
  365. UINT Message,
  366. WPARAM wParam,
  367. LPARAM lParam
  368. )
  369. {
  370. switch (Message)
  371. {
  372. case WM_INITDIALOG:
  373. cwCenter(hWndDlg, 0);
  374. vcchPrev = 0;
  375. vcpIpPrev = 0;
  376. vhWndDlg = hWndDlg;
  377. break;
  378. case WM_CLOSE:
  379. break;
  380. case WM_COMMAND:
  381. switch (LOWORD(wParam))
  382. {
  383. case IDC_EDIT1:
  384. switch (HIWORD(wParam))
  385. {
  386. case EN_CHANGE:
  387. vcchEditText = GetDlgItemText(hWndDlg, IDC_EDIT1, vszEditText,
  388. cbEditText);
  389. if (!vfSettingDlgItem)
  390. {
  391. MSOCP cpIp;
  392. XCHAR wch;
  393. /* Get IP (Insertion Point) position */
  394. SendDlgItemMessage(hWndDlg, IDC_EDIT1, EM_GETSEL, 0,
  395. (LPARAM)&cpIp);
  396. /* Get character typed */
  397. wch = (vcchEditText > vcchPrev
  398. ? vszEditText[cpIp - 1] // Char typed
  399. : xchBackspace); // Backspace
  400. /* Notify Event Monitor CHAR driver */
  401. FEmEvalChar(wch, cpIp, vszEditText, vcchEditText);
  402. /* Track prev text positions for invalidation detect */
  403. vcchPrev = vcchEditText;
  404. vcpIpPrev = cpIp;
  405. }
  406. break;
  407. }
  408. break;
  409. case IDC_BUTTON1:
  410. case IDC_BUTTON2:
  411. case IDC_START:
  412. case IDC_STOP:
  413. default:
  414. FEmEvalDialog(wParam);
  415. break;
  416. case IDOK:
  417. FEmEvalDialog(wParam);
  418. EndDialog(hWndDlg, FALSE);
  419. break;
  420. }
  421. break;
  422. default:
  423. return FALSE;
  424. }
  425. return TRUE;
  426. }
  427. #endif // }
  428. #if FEATURE_SAMPLE // { joe event firer
  429. /* F E M E V A L D I A L O G */
  430. /*----------------------------------------------------------------------------
  431. %%Function: FEmEvalDialog
  432. %%Contact: daleg
  433. Event Monitor Event Driver for DIALOG events.
  434. ----------------------------------------------------------------------------*/
  435. int FEmEvalDialog(WPARAM wParam)
  436. {
  437. short idc = LOWORD(wParam);
  438. IRUL irul;
  439. #if FEATURE_DEMO
  440. /* Make sure we are enabled (e.g. macros not running) */
  441. if (FEmDisabled())
  442. return fFalse;
  443. #endif
  444. /* Match the (sparse) dialog object to its associated (contig) event, if any */
  445. vlpruls->irulPrimaryEvent = irul
  446. = (IRUL) MsoPkwdlhLookupL(idc, &vkwtbEmIDC_KEYTABLE)->tk;
  447. debugEM1(2, "DIALOG EVENT: %-20.20s\n",
  448. LpchRulName(LprulFromIrul(irul)));
  449. /* Push event into appropriate queue (resets depth) */
  450. MsoScheduleIrul(irul, fTrue);
  451. MsoScheduleIrul(irulIDC_, idc); // non-mapped event (autoclear global)
  452. /* Propagate the values through the rule network */
  453. MsoEvaluateEvents(rulevtEmDIALOG);
  454. // n.b. IDC_ is now autocleared
  455. /* Evaluate any pending actions */
  456. if (_pacttbl->pactPending)
  457. DoPendingActions();
  458. }
  459. /* F E M E V A L C H A R */
  460. /*----------------------------------------------------------------------------
  461. %%Function: FEmEvalChar
  462. %%Contact: daleg
  463. Event Driver for CHAR events in Event Monitor.
  464. Evaluate CHAR (KEYBOARD) events within the Event Monitor rulebase.
  465. Return whether or not it was handled.
  466. ----------------------------------------------------------------------------*/
  467. int FEmEvalChar(
  468. XCHAR wch,
  469. MSOCP cpIp,
  470. XCHAR *wz,
  471. int ichMac
  472. )
  473. {
  474. IRUL irul;
  475. #if FEATURE_DEMO
  476. /* Make sure we are enabled (e.g. macros not running) */
  477. if (FEmDisabled())
  478. return fFalse;
  479. #endif
  480. /* Primitive invalidation */
  481. if (FCheckIfMustReset(wch, ichMac, cpIp))
  482. InvalLex(vplexs);
  483. /* If our running state is still valid, adjust run variables */
  484. if (FInvalLex(vplexs))
  485. FResetEm((vplexs->cpIp = cpIp) - 1, wz, ichMac);
  486. /* Create a "lookahead" token to hold chars not yet scanned by lexer */
  487. vplexs->cchLookahead++;
  488. _CacheTkTextNext(vplexs);
  489. /* Look up character in keyword table */
  490. irul = (IRUL) MsoPkwdLookupName(&wch, 1, &vkwtbEmCHAR_KEYTABLE)->tk;
  491. debugEM3(2, "CHAR EVENT: %-20.20s for '%s' (0x%x)\n",
  492. LpchRulName(LprulFromIrul(irul)),
  493. MsoSzFromRgxchDebug(&wch, 1), wch);
  494. /* Push text token into appropriate queue (resets depth) */
  495. MsoScheduleIrul(irulCH_, wch);
  496. MsoScheduleIrul(vlpruls->irulPrimaryEvent = irul, fTrue);
  497. /* Propagate the values through the rule network */
  498. MsoEvaluateEvents(rulevtEmCHAR);
  499. /* Call TOKEN event driver to process any token events */
  500. FEmEvalToken(cpIp, wz, ichMac);
  501. }
  502. /* E M E V A L T K I R U L */
  503. /*----------------------------------------------------------------------------
  504. %%Function: EmEvalTkIrul
  505. %%Contact: daleg
  506. Evaluate a TOKEN event irul.
  507. ----------------------------------------------------------------------------*/
  508. void EmEvalTkIrul(IRUL irul)
  509. {
  510. MSORULTK *prultk;
  511. debugEM2(2, "TOKEN EVENT: %-20.20s \"%.100s\"\n",
  512. LpchIrulName(irul), MsoSzLexTokenText(vplexs));
  513. /* Push pending token events into appropriate queues */
  514. prultk = PrultkFromTokenIrultk(vplexs, vplexs->rultkhToken.irultkMin);
  515. while (vplexs->rultkhToken.irultkMin != vplexs->rultkhToken.irultkLim)
  516. {
  517. #ifdef DEBUG
  518. if (prultk->tk != irul)
  519. {
  520. debugEM2(4, "EXTRA TOKEN EVENT: %-20.20s %d\n",
  521. LpchIrulName(prultk->tk), prultk->lValue);
  522. debugEM1(8, " at CP %ld\n", prultk->cpFirst);
  523. }
  524. #endif /* DEBUG */
  525. /* Push text token into appropriate queue (resets depth) */
  526. MsoSignalEventIrul(IrulFromTk(prultk->tk), prultk->lValue);
  527. /* Move to next cache record, wrapping around if necessary */
  528. IncrTokenPrultk(vplexs, &prultk, &vplexs->rultkhToken.irultkMin);
  529. }
  530. /* Push any applicable format tokens into appropriate queues */
  531. if (vplexs->rultkhFormat.irultkMin != vplexs->rultkhFormat.irultkLim)
  532. PushIrultkFormatPending();
  533. /* Push text token into appropriate queue (resets depth) */
  534. MsoScheduleIrul(irulTOKEN_, vlpruls->irulPrimaryEvent = irul);
  535. /* Propagate the values through the rule network */
  536. MsoEvaluateEvents(rulevtEmTOKEN);
  537. }
  538. #endif // }
  539. #if FEATURE_SAMPLE // {
  540. /* F E M E V A L T O K E N */
  541. /*----------------------------------------------------------------------------
  542. %%Function: FEmEvalToken
  543. %%Contact: daleg
  544. Event Monitor Event Driver for TOKEN events.
  545. ----------------------------------------------------------------------------*/
  546. int FEmEvalToken(
  547. MSOCP cpIp,
  548. XCHAR *wz,
  549. int ichMac
  550. )
  551. {
  552. IRUL irul;
  553. #if FEATURE_DEMO
  554. /* Make sure we are enabled (e.g. macros not running) */
  555. if (FEmDisabled())
  556. return fFalse;
  557. #endif
  558. /* If our running state is still valid, adjust run variables */
  559. if (!FInvalLex(vplexs))
  560. {
  561. long dwz = wz - vplexs->pxchBuffer;
  562. vplexs->pxchTkStart += dwz;
  563. vplexs->pxchNext += dwz;
  564. vplexs->pxchRun += dwz;
  565. vplexs->pxchBuffer += dwz;
  566. vplexs->cpIp = cpIp;
  567. vplexs->cpLim = ichMac;
  568. vplexs->cchRemain = vplexs->pxchBuffer + vplexs->cpLim
  569. - vplexs->pxchNext;
  570. }
  571. /* Else rescan back up to IP */
  572. else if (!FResetEm((vplexs->cpIp = cpIp) - 1, wz, ichMac))
  573. return fFalse;
  574. /* Inject and eval any complete tokens up to IP */
  575. while (FValidIrul(irul = IrulFromTk(MsoTkLexTextCpLim(vplexs, cpIp))))
  576. EmEvalTkIrul(irul);
  577. /* Evaluate any pending actions */
  578. if (_pacttbl->pactPending)
  579. DoPendingActions();
  580. /* All typed characters must now have been seen by the lexer */
  581. if (vplexs->cchLookahead > 0)
  582. vplexs->cchLookahead = 0;
  583. }
  584. #endif // }
  585. #if FEATURE_TEXT // {
  586. /* P U S H I R U L T K F O R M A T P E N D I N G */
  587. /*----------------------------------------------------------------------------
  588. %%Function: PushIrultkFormatPending
  589. %%Contact: daleg
  590. Push any pending format tokens into appropriate queues.
  591. ----------------------------------------------------------------------------*/
  592. void PushIrultkFormatPending(void)
  593. {
  594. MSORULTK *prultk;
  595. debugEM1(8, "Checking for formatting before CP %ld\n",
  596. CpLexTokenFirst(vplexs));
  597. prultk = PrultkFormatFromIrultk(vplexs, vplexs->rultkhFormat.irultkMin);
  598. while (vplexs->rultkhFormat.irultkMin != vplexs->rultkhFormat.irultkLim
  599. && (prultk->cpFirst
  600. < CpLexTokenFirst(vplexs) + DcpLexToken(vplexs)
  601. || (DcpLexToken(vplexs) == 0
  602. && prultk->cpFirst <= CpLexTokenFirst(vplexs))))
  603. {
  604. debugEM2(2, "FORMAT : %-20.20s %ld\n",
  605. LpchIrulName(prultk->tk), prultk->lValue);
  606. debugEM1(6, " at CP %ld\n", prultk->cpFirst);
  607. /* Push format token into appropriate queue (resets depth) */
  608. MsoSignalEventIrul(IrulFromTk(prultk->tk), prultk->lValue);
  609. /* Move to next cache record, wrapping around if necessary */
  610. IncrFormatPrultk(vplexs, &prultk, &vplexs->rultkhFormat.irultkMin);
  611. }
  612. }
  613. #endif // }
  614. #if FEATURE_TEXT // {
  615. /* F R E S E T E M */
  616. /*----------------------------------------------------------------------------
  617. %%Function: FResetEm
  618. %%Contact: daleg
  619. Reset the Event Monitor rules and lexer due to an IP (cursor) change,
  620. or due to some form of invalidation.
  621. ----------------------------------------------------------------------------*/
  622. int FResetEm(
  623. MSOCP cpIp,
  624. XCHAR *wz,
  625. int ichMac
  626. )
  627. {
  628. MSOCP cpObject = 0;
  629. IRUL irul;
  630. /* Initialize lexer to point to start of buffer */
  631. vplexs->pObjectIp = PObjectCur();
  632. vplexs->pxchBuffer = vplexs->pxchBufferIp = vplexs->pxchRun = wz;
  633. MsoLexSetPos(vplexs, cpObject, 0);
  634. debugEM0(0, "====================================================\n");
  635. debugEM3(0, " FResetEm: RESETTING: pObjectIp %x cpLine %ld cp %ld\n",
  636. vplexs->pObjectIp, cpObject, cpIp);
  637. debugEM0(0, "====================================================\n");
  638. /* Reset rule base TOKEN state variables */
  639. MsoClearEventsForRulevts(rulevtEmTOKEN, drulevtToken,
  640. (vpems->fInternalReset
  641. ? rultPersistentRule | rultAlwaysPersist
  642. : rultAlwaysPersist),
  643. fTrue, fFalse);
  644. vplexs->wInterval = CIntervalsRulevt(rulevtEmTOKEN);
  645. /* Reset lexer base state */
  646. MsoResetLexState(vplexs, fTrue/*fFullReset*/);
  647. vplexs->cpLim = 0;
  648. vplexs->fInvalLexer = fFalse;
  649. /* Mark start of scan */
  650. MsoCacheTkText(vplexs, irulSTART, fTrue);
  651. MsoScheduleIrul(irulSTART, fTrue);
  652. /* Mark start of token cache, but not as events */
  653. MsoCacheTkText(vplexs, irulEND_OBJ, fTrue);
  654. vplexs->rultkhToken.irultkMin = vplexs->rultkhToken.irultkLim;
  655. /* Run only rules not marked as INTERACTIVE_ONLY */
  656. SetCurrRulg(rulgEmALWAYS);
  657. /* Inject and eval any complete tokens up to IP */
  658. while (FValidIrul(irul = IrulFromTk(MsoTkLexTextCpLim(vplexs, cpIp))))
  659. EmEvalTkIrul(irul);
  660. /* Run only rules not marked as INTERACTIVE_ONLY */
  661. SetCurrRulg(rulgEmINTERACTIVE_ONLY);
  662. /* Mark that we are synchronized */
  663. vplexs->cchLookahead = 0;
  664. return fTrue;
  665. }
  666. /* F G E T T O K E N T E X T O B J E C T */
  667. /*----------------------------------------------------------------------------
  668. FGetTokenTextObject
  669. %%Contact: smueller
  670. Determine whether the text of the requested token needs to be fetched
  671. from the document. If so, do so, leaving the results in ppxch and pcch,
  672. and return fTrue. Otherwise, return fFalse.
  673. ----------------------------------------------------------------------------*/
  674. int WIN_CALLBACK FGetTokenTextObject(
  675. MSORULTK *prultk,
  676. const XCHAR **ppxch, // RETURN
  677. int *pcch, // RETURN
  678. struct _MSOLEXS *plexs
  679. )
  680. {
  681. if (plexs->pObject != NULL)
  682. {
  683. // NOT SUPPORTED YET
  684. return fTrue;
  685. }
  686. return fFalse;
  687. }
  688. /* F G E T N E X T L E X R U N */
  689. /*----------------------------------------------------------------------------
  690. %%Function: FGetNextLexRun
  691. %%Contact: daleg
  692. Return next run of text within the current object , and set the lexer
  693. run-state variables.
  694. For this demo, there ain't any, but we will show a commented-out sample.
  695. ----------------------------------------------------------------------------*/
  696. int WIN_CALLBACK FGetNextLexRun(MSOCP cpLim, MSOLEXS *plexs)
  697. {
  698. int fStatus = fTrue;
  699. const XCHAR *pxchPrevEnd = plexs->pxchNext;
  700. /* If prev run at end of current object, move to next object */
  701. if (FLexEndOfScan(vplexs))
  702. {
  703. /* If still have a pending token, complete it first */
  704. if (DcpLexCurr(vplexs) > 0)
  705. return fFalse;
  706. /* Create exactly one inter-object "created" tkEND_OBJ character */
  707. if (plexs->fCreateEndObjCh)
  708. {
  709. /* Make this run "created" text, i.e. no dcp */
  710. fStatus = fFalse;
  711. plexs->cpRun += plexs->ccpRun;
  712. plexs->ichRun += plexs->cchRun;
  713. plexs->cchRun = 1;
  714. plexs->ccpRun = 0;
  715. #ifdef READY
  716. /* Mark run as "created", so dcp calcs will work properly */
  717. plexs->fAdjustTokenCps = fTrue;
  718. plexs->dcpCreated += 1;
  719. if (plexs->cpFirstCreated == 0L)
  720. plexs->cpFirstCreated = plexs->cpRun;
  721. #endif /* READY */
  722. }
  723. /* Else get next object's text */
  724. else
  725. {
  726. if (!FGetNextLexObject(cpLim, plexs))
  727. return fFalse;
  728. /* If in object of IP has passed upper lim of scan, block lexer */
  729. if (plexs->pObject == plexs->pObjectIp && cpLim != msocpMax
  730. && cpLim <= 0 /* plexs->cpRun */)
  731. fStatus = fFalse;
  732. }
  733. }
  734. /* Else move to next run */
  735. else
  736. {
  737. plexs->cpRun += plexs->ccpRun;
  738. plexs->ichRun += plexs->cchRun;
  739. #ifdef PORT_THIS
  740. plexs->cchRun = vcchEditText - plexs->ichRun;
  741. #endif /* PORT_THIS */
  742. plexs->ccpRun = plexs->cchRun;
  743. // Reset buffer pointer if run in a different buffer
  744. #ifdef PORT_THIS
  745. plexs->pxchBuffer = vszEditText;
  746. #endif /* PORT_THIS */
  747. }
  748. /* Set Run length and pointer */
  749. plexs->cchRemain = plexs->cchRun;
  750. plexs->pxchNext = (plexs->pxchRun = plexs->pxchBuffer + plexs->ichRun);
  751. AssertSz0(pxchPrevEnd == plexs->pxchNext
  752. || pxchPrevEnd == plexs->pxchTkStart,
  753. "Discontiguous runs!");
  754. /* If run starts new text (line), reset non-cached tk start pointer */
  755. if (pxchPrevEnd == plexs->pxchTkStart)
  756. plexs->pxchTkStart = plexs->pxchNext;
  757. return fStatus;
  758. }
  759. /* F G E T N E X T L E X O B J E C T */
  760. /*----------------------------------------------------------------------------
  761. %%Function: FGetNextLexObject
  762. %%Contact: daleg
  763. Set the "run" state variables to the first run of a new object, including
  764. the text buffer, and the run lengths.
  765. ----------------------------------------------------------------------------*/
  766. int WIN_CALLBACK FGetNextLexObject(MSOCP cpLim, MSOLEXS *plexs)
  767. {
  768. // If this is the first time thru, do initialization stuff
  769. if (plexs->ichRun == iNil)
  770. {
  771. // YOUR INITS HERE
  772. }
  773. // Fetch new object text
  774. plexs->pObject = PObjectCur();
  775. // Fetch new text (in our case, it is globally static)
  776. plexs->pxchBuffer = vszEditText;
  777. plexs->cchRun = plexs->cpLim = vcchEditText;
  778. plexs->pxchRun = plexs->pxchBuffer;
  779. // Set up other state variables
  780. plexs->ichRun = 0;
  781. plexs->cpRun = 0;
  782. plexs->cpObject = 0;
  783. plexs->ccpRun = plexs->cchRun;
  784. SetCpLexTokenFirst(vplexs, SetCpLexTokenNext(vplexs, 0));
  785. return fTrue;
  786. }
  787. /* F L E X F O R C E C O M P L E T E */
  788. /*----------------------------------------------------------------------------
  789. %%Function: FLexForceComplete
  790. %%Contact: daleg
  791. Force the current token to complete within the lexer. This is a callback
  792. function that gets called when we wish to cause the lexer to finish a
  793. token without peeking at the next character. This is generally when we
  794. are moving the IP out of a cell in a table, and wish to perform automatic
  795. actions anyway.
  796. Once we have completed the token, we clear the callback flag, to resume
  797. normal operation.
  798. ----------------------------------------------------------------------------*/
  799. int WIN_CALLBACK FLexForceComplete(MSOCP cpLim, MSOLEXS *plexs)
  800. {
  801. XCHAR wch;
  802. /* If in the middle of a token complete it if next is EOO or delim */
  803. // REVIEW daleg: This should check for more than just spaces after
  804. if (plexs->iuState > 0
  805. && (cpLim == vplexs->cpLim
  806. || (wch = *vplexs->pxchNext) == xchSpace))
  807. return fTrue;
  808. /* Do not call this routine next time */
  809. plexs->pfnlexrunForceComplete = NULL;
  810. /* Force an END_OBJ token (event) if we are at the cell boundary */
  811. return (cpLim == vplexs->cpLim);
  812. }
  813. #endif // }
  814. #if YY_BASE // {
  815. /* P A C T P C A */
  816. /*----------------------------------------------------------------------------
  817. %%Function: PactPca
  818. %%Contact: daleg
  819. Create a Delayed-Action record, using a MSOCA to define the edit range.
  820. ----------------------------------------------------------------------------*/
  821. MSOACT *PactPca(
  822. MSOACTTBL *pacttbl,
  823. int actt,
  824. MSOCA *pca,
  825. ...
  826. )
  827. {
  828. va_list ap;
  829. MSOACT *pact;
  830. /* Safety first: make sure we have an action structure */
  831. if (!pacttbl)
  832. return NULL;
  833. /* Start varargs */
  834. va_start(ap, pca);
  835. /* Create a new Delayed Action record, and push arg list into it */
  836. pact = MsoPactAp(pacttbl, actt,
  837. (sizeof(MSOCA) + sizeof(long) - 1)/sizeof(long), ap);
  838. /* End varargs */
  839. va_end(ap);
  840. /* Calculate starting CP */
  841. pact->rec1_ca.ca = *pca;
  842. /* Insert the MSOACT record into the edit queue in CP sorted order */
  843. MsoInsertPact(pact, &pacttbl->pactPending);
  844. return pact;
  845. }
  846. #if YY_DELAYED // {
  847. long WIN_CALLBACK DcpDoAct(
  848. MSOACT *pact,
  849. MSOACTTBL *pacttbl,
  850. long *pdcp,
  851. MSOCA *pca,
  852. MSOACT **ppactNext,
  853. int *pfDiscard
  854. );
  855. /* D O P E N D I N G A C T I O N S */
  856. /*----------------------------------------------------------------------------
  857. %%Function: DoPendingActions
  858. %%Contact: daleg
  859. Execute pending actions in delay action queue.
  860. ----------------------------------------------------------------------------*/
  861. void DoPendingActions(void)
  862. {
  863. _pacttbl->cpFirstEditPrev = -1;
  864. _pacttbl->dcpEditPrev = 0;
  865. _pacttbl->cpLimEdit = 0;
  866. MsoReversePact(&_pacttbl->pactPending);
  867. MsoDcpDoActs(&_pacttbl->pactPending, _pacttbl, 0, fTrue, -1, DcpDoAct);
  868. }
  869. /* D C P D O A C T */
  870. /*----------------------------------------------------------------------------
  871. %%Function: DcpDoAct
  872. %%Contact: daleg
  873. Execute the action given by the MSOACT record.
  874. ----------------------------------------------------------------------------*/
  875. long WIN_CALLBACK DcpDoAct(
  876. MSOACT *pact,
  877. MSOACTTBL *pacttbl,
  878. long *pdcp,
  879. MSOCA *pca,
  880. MSOACT **ppactNext,
  881. int *pfDiscard
  882. )
  883. {
  884. switch (pact->rec1.actt)
  885. {
  886. #include "emact.c_"
  887. }
  888. return 0;
  889. }
  890. #endif // }
  891. #endif // }
  892. #if FEATURE_DEMO // {
  893. /* D C P R E P L A C E T E X T */
  894. /*----------------------------------------------------------------------------
  895. %%Function: DcpReplaceText
  896. %%Contact: daleg
  897. Replace the text range given by the range of chars with the new string.
  898. ----------------------------------------------------------------------------*/
  899. int DcpReplaceText(
  900. void *pObject,
  901. int cpFirst,
  902. int cpLim,
  903. char *sz
  904. )
  905. {
  906. int cch = CchSz(sz);
  907. int dcp = cch - (cpLim - cpFirst);
  908. int cchOrig;
  909. char rgch[cbEditText];
  910. MSOCP cpIp;
  911. /* Get current edit control value */
  912. cchOrig = GetDlgItemText(vhWndDlg, IDC_EDIT1, rgch, cbEditText);
  913. /* Do not attempt to expand beyond control's capacity. */
  914. if (cchOrig + dcp >= cbEditText)
  915. return 0;
  916. /* Replace the string */
  917. CopyRgb(rgch + cpLim, rgch + cpLim + dcp, cchOrig - cpLim + 1);
  918. CopyRgbNo(sz, rgch + cpFirst, cch);
  919. /* Set the edit control value with the new string, preserving the IP */
  920. vfSettingDlgItem = fTrue; // Prevent recursion
  921. SendDlgItemMessage(vhWndDlg, IDC_EDIT1, EM_GETSEL, 0, (LPARAM)&cpIp);
  922. SendDlgItemMessage(vhWndDlg, IDC_EDIT1, WM_SETTEXT, 0, (LPARAM)&rgch[0]);
  923. SendDlgItemMessage(vhWndDlg, IDC_EDIT1, EM_SETSEL, cpIp + dcp, cpIp + dcp);
  924. vcchEditText += dcp;
  925. vfSettingDlgItem = fFalse;
  926. debugEM0(2, "Replaced text\n");
  927. return dcp;
  928. }
  929. #endif // }
  930. #if FEATURE_TEXT // {
  931. /* F C H E C K I F M U S T R E S E T */
  932. /*----------------------------------------------------------------------------
  933. %%Function: FCheckIfMustReset
  934. %%Contact: daleg
  935. Do a primitive check to see if we should invalidate our running rulebase
  936. state, and do a full reset scan.
  937. THIS IS NOT A CANONICAL ROUTINE, but there should be something equivalent
  938. in each app.
  939. ----------------------------------------------------------------------------*/
  940. int FCheckIfMustReset(XCHAR wch, int ichMac, MSOCP cpIp)
  941. {
  942. if (wch == xchBackspace)
  943. return fTrue;
  944. /* If user typed a new character, emit character event */
  945. /* REVIEW: pasting will result in multiple new characters, of which we
  946. only notice the last */
  947. if (ichMac > vcchPrev)
  948. {
  949. Assert (cpIp > 0);
  950. /* Without notification of IP moves, there is no way to accurately
  951. identify when lexer needs invalidating; for safety, we could do so
  952. on every character. For demonstrating the lexer, we'll do so only
  953. when the IP didn't move forward by exactly one character, which is
  954. a decent approximation of the right time. */
  955. /* REVIEW: find a more pleasant way to deal with this, like subclassing
  956. the edit control to get precise control over notifications. */
  957. if (cpIp != vcpIpPrev + 1)
  958. return fTrue;
  959. }
  960. return fFalse;
  961. }
  962. #endif // }
  963. #if FEATURE_DEMO // {
  964. /* N C W R E G I S T E R C L A S S E S */
  965. /*----------------------------------------------------------------------------
  966. %%Function: nCwRegisterClasses
  967. %%Contact: daleg
  968. Register window classes
  969. ----------------------------------------------------------------------------*/
  970. int nCwRegisterClasses(void)
  971. {
  972. WNDCLASS wndclass;
  973. memset(&wndclass, 0x00, sizeof(WNDCLASS));
  974. /* load WNDCLASS with window's characteristics */
  975. wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW;
  976. wndclass.lpfnWndProc = WndProc;
  977. /* Extra storage for Class and Window objects */
  978. wndclass.cbClsExtra = 0;
  979. wndclass.cbWndExtra = 0;
  980. wndclass.hInstance = vhInst;
  981. wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  982. wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  983. /* Create brush for erasing background */
  984. wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  985. wndclass.lpszMenuName = vszAppName; /* Menu Name is App Name */
  986. wndclass.lpszClassName = vszAppName; /* Class Name is App Name */
  987. if (!RegisterClass(&wndclass))
  988. return -1;
  989. return (0);
  990. }
  991. /* C W C E N T E R */
  992. /*----------------------------------------------------------------------------
  993. %%Function: cwCenter
  994. %%Contact: daleg
  995. Center the main window.
  996. ----------------------------------------------------------------------------*/
  997. void cwCenter(hWnd, top)
  998. HWND hWnd;
  999. int top;
  1000. {
  1001. POINT pt;
  1002. RECT swp;
  1003. RECT rParent;
  1004. int iwidth;
  1005. int iheight;
  1006. /* get the rectangles for the parent and the child */
  1007. GetWindowRect(hWnd, &swp);
  1008. GetClientRect(vhWndMain, &rParent);
  1009. /* calculate the height and width for MoveWindow */
  1010. iwidth = swp.right - swp.left;
  1011. iheight = swp.bottom - swp.top;
  1012. /* find the center point and convert to screen coordinates */
  1013. pt.x = (rParent.right - rParent.left) / 2;
  1014. pt.y = (rParent.bottom - rParent.top) / 2;
  1015. ClientToScreen(vhWndMain, &pt);
  1016. /* calculate the new x, y starting point */
  1017. pt.x = pt.x - (iwidth / 2);
  1018. pt.y = pt.y - (iheight / 2);
  1019. /* top will adjust the window position, up or down */
  1020. if (top)
  1021. pt.y = pt.y + top;
  1022. /* move the window */
  1023. MoveWindow(hWnd, pt.x, pt.y, iwidth, iheight, FALSE);
  1024. }
  1025. /* C W U N R E G I S T E R C L A S S E S */
  1026. /*----------------------------------------------------------------------------
  1027. %%Function: CwUnRegisterClasses
  1028. %%Contact: daleg
  1029. Un-register the windows classes.
  1030. ----------------------------------------------------------------------------*/
  1031. void CwUnRegisterClasses(void)
  1032. {
  1033. WNDCLASS wndclass;
  1034. memset(&wndclass, 0x00, sizeof(WNDCLASS));
  1035. UnregisterClass(vszAppName, vhInst);
  1036. }
  1037. #endif // }
  1038. #if FEATURE_DEAD // {
  1039. /* A S S E R T L S Z P R O C */
  1040. /*----------------------------------------------------------------------------
  1041. %%Function: AssertLszProc
  1042. %%Contact: daleg
  1043. Print assertion message, and prompt for whether to (f)ail, or (i)gnore.
  1044. ----------------------------------------------------------------------------*/
  1045. int AssertLszProc(
  1046. const char *szExtra,
  1047. const char *szFile,
  1048. int line
  1049. )
  1050. {
  1051. Fail("ASSERTION FAILED: %s IN %s line %d\n", szExtra, szFile, line);
  1052. return 1;
  1053. }
  1054. /* F A I L */
  1055. /*----------------------------------------------------------------------------
  1056. %%Function: Fail
  1057. %%Contact: daleg
  1058. Emit a failure message and exit.
  1059. ----------------------------------------------------------------------------*/
  1060. void __cdecl Fail(const char *sz, ...)
  1061. {
  1062. va_list ap;
  1063. char szBuf[256];
  1064. /* Start variable arglist */
  1065. va_start(ap, sz);
  1066. wvsprintf(szBuf, sz, ap);
  1067. OutputDebugStringA("FATAL ERROR: ");
  1068. OutputDebugStringA(szBuf);
  1069. OutputDebugStringA("\n");
  1070. /* End variable arglist */
  1071. va_end(ap);
  1072. // _asm int 3;
  1073. exit(1);
  1074. }
  1075. #endif // }
  1076. #if FEATURE_DEAD // {
  1077. /* F N E N C L P C H */
  1078. /*----------------------------------------------------------------------------
  1079. %%Function: FNeNcLpch
  1080. %%Contact: daleg
  1081. Compare two strings, case insensitive.
  1082. ----------------------------------------------------------------------------*/
  1083. BOOL FNeNcLpch(
  1084. register const uchar *pch1,
  1085. register const uchar *pch2,
  1086. register int cch
  1087. )
  1088. {
  1089. while (cch-- > 0)
  1090. {
  1091. if (ChUpper(*pch1++) != ChUpper(*pch2++))
  1092. return fTrue;
  1093. }
  1094. return fFalse;
  1095. }
  1096. #endif // }