1374 lines
41 KiB
C
1374 lines
41 KiB
C
/*----------------------------------------------------------------------------
|
|
%%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 // }
|