Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2718 lines
62 KiB

/*****************************************************************************
* *
* INDEX.CPP *
* *
* Copyright (C) Microsoft Corporation 1993-1994 *
* All Rights reserved. *
* *
* This file is #included in HDLGSRCH.CPP
*
*****************************************************************************/
extern "C" {
#include "help.h"
}
#pragma hdrstop
#include "inc\whclass.h"
#include <ctype.h>
#include <io.h>
#include <direct.h>
#include <stdio.h>
#include <shellapi.h>
#include "inc\table.h"
#include "inc\hwproc.h"
#include "inc\hdlgsrch.h"
#include "inc\input.h"
#include "inc\helpids.h"
#include "inc\systag.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
static const int LISTBOX_PAD = 12;
// #define TRUNCATED_KEYWORD
#ifndef NO_PRAGMAS
#pragma data_seg(".text", "CODE")
#endif
const char txtMAPNAME[] = "|KWMAP";
// This is for context-sensitive help
static DWORD aKeywordIds[] = {
DLGEDIT, IDH_HELPFINDER_INDEX,
DLGVLISTBOX, IDH_HELPFINDER_INDEX,
IDC_DUP_TEXT, IDH_HELPFINDER_MULTIPLETOPICS,
DLGTOPICS, IDH_HELPFINDER_MULTIPLETOPICS,
IDOK, IDH_HELPFINDER_DISPLAY,
0, 0
};
#ifndef NO_PRAGMAS
#pragma data_seg()
#endif
#ifdef STRICT
WNDPROC lpfnlEditWndProc;
#else
FARPROC lpfnlEditWndProc;
#endif
static CTable* ptblLinks;
/*****************************************************************************
* *
* Prototypes *
* *
*****************************************************************************/
static void STDCALL PutUpSearchErrorBox(HWND, RC);
INLINE static void STDCALL LookForAlinks(PSTR pszLinkWords, char chPrefix, UINT flags);
static BOOL STDCALL doAlinkJump(int DlgResult, PSTR pszWindow, char chPrefix);
DLGRET ALinkDlg(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
static void STDCALL AdjustGlobalIndex(HWND hwndDlg);
static void STDCALL ResizeTopicsDialog(HWND hwndDlg, HWND hwndLB, int cbMax);
LRESULT EXPORT EditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
static void STDCALL ReportAlinkResult(BOOL fSuccess, char chPrefix, PCSTR pszWords);
static BOOL STDCALL FillTopicBox(HDE hde, HSS hss, HWND hwnd, HFS hfsMaster, PSTR szKeyword);
const int VPAD = 10; // padding between bottom of list box and top of checkbox
// REVIEW: still necessary to turn off optimization?
DLGRET IndexDlg(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
HWND hwndListBox;
char szKeyTemp[MAXKEYLEN];
int i;
switch(msg) {
#if defined(BIDI_MULT)
case WM_LANGUAGE:
DefWindowProc(hwndDlg, msg, wParam, lParam);
if (wParam != -1) {
RtoL = (wParam == Arabic) || (wParam == Hebrew);
MakeScrollBarsRtoL(GetDlgItem(hwndDlg, DLGVLISTBOX),
RtoL, TRUE);
}
break;
#endif
case WM_INITDIALOG:
{
CWaitCursor cursor;
#if defined(BIDI_MULT) // jgross
{
DWORD ResLang;
SystemParametersInfo(SPI_GETMULTILINGUAL, 0, &ResLang, 0);
RtoL = ((HIWORD(ResLang) == Arabic) || (HIWORD(ResLang) == Hebrew));
if (HIWORD(ResLang) == Russian)
SetAppCodePage(GetDlgItem(hWndDlg, DLGEDIT),
RussianCodePage, -1, 0);
}
#endif
ChangeDlgFont(hwndDlg);
ASSERT(hfontDefault);
SendMessage(GetDlgItem(hwndDlg, DLGVLISTBOX), WM_SETFONT,
(WPARAM) hfontDefault, FALSE);
SendMessage(GetDlgItem(hwndDlg, DLGEDIT), WM_SETFONT,
(WPARAM) hfontDefault, FALSE);
// Subclass the edit control
HWND hwndEdit = GetDlgItem(hwndDlg, DLGEDIT);
ASSERT(hwndEdit);
if (lpfnlEditWndProc == NULL)
#ifdef STRICT
lpfnlEditWndProc = (WNDPROC) GetWindowLong(hwndEdit,
GWL_WNDPROC);
#else
lpfnlEditWndProc = (FARPROC) GetWindowLong(hwndEdit,
GWL_WNDPROC);
#endif
SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG) EditProc);
#if defined(BIDI_MULT)
MakeScrollBarsRtoL(GetDlgItem(hWndDlg, DLGVLISTBOX), RtoL, TRUE);
#endif
// Do we have multiple HLP files to draw from?
if (hfsGid && (cntFlags.flags & GID_GINDEX)) {
#ifdef _DEBUG
char szBuf[256];
wsprintf(szBuf, "LCID: 0x%X\r\n", cntFlags.lcid);
SendStringToParent(szBuf);
#endif
lcid = cntFlags.lcid;
}
pSrchClass->dwTop = 0;
if (!pSrchClass->InitIndexDlg(hwndDlg)) {
SendMessage(GetParent(hwndDlg), WM_COMMAND,
ID_NO_INDEX, 0);
}
}
return TRUE;
case WM_HELP:
OnF1Help(lParam, aKeywordIds);
return TRUE;
case WM_CONTEXTMENU: // user right-clicked something
OnContextMenu(wParam, aKeywordIds);
return TRUE;
case WM_MEASUREITEM:
{
#define lpmi ((LPMEASUREITEMSTRUCT)lParam)
TM tm;
// Return the height of the font for this list box
hwndListBox = GetDlgItem(hwndDlg, DLGVLISTBOX);
ASSERT(hwndListBox);
hdc = GetDC(hwndListBox);
if (!hdc) {
PutUpSearchErrorBox(hwndDlg, rcOutOfMemory);
return FALSE;
}
GetTextMetrics(hdc, &tm);
lpmi->itemHeight = tm.tmHeight - 3;
ReleaseDC(hwndListBox, hdc);
#undef lpmi
}
return TRUE;
case WM_DRAWITEM:
return pSrchClass->OnDrawItem((LPDRAWITEMSTRUCT) lParam);
case WM_DELETEITEM:
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case DLGEDIT:
if ((HIWORD(wParam) != EN_CHANGE))
break;
/*
* Do the auto-scroll each time the editbox changes but not
* if this was generated by a selection change
*/
hwndListBox = GetDlgItem(hwndDlg, DLGVLISTBOX);
ASSERT(hwndListBox);
GetDlgItemText(hwndDlg, DLGEDIT, pSrchClass->szKeyword,
MAXKEYLEN);
if (!pSrchClass->fSelectionChange) {
BTPOS btpos;
/*
* Look up whatever is in the edit control We don't care
* if this returns SUCCESS or not.
*/
RcLookupByKey(CUR_HBT, (KEY) pSrchClass->szKeyword,
&btpos, NULL);
/*
* If we ran off the end, then position ourselves at the
* last key in the btree and go from there.
*/
if (!FValidPos(&btpos)) {
RcLastHbt(CUR_HBT, (KEY)NULL, NULL, &btpos);
pSrchClass->dwTemp = pSrchClass->cItems - 1;
}
else {
/*
* We are somewhere in the btree. We have either
* typed in a string which is a prefix to a keyword in
* the btree or not. See where we landed in the btree
* and compare the keyword in the dialog with where we
* are.
*/
RcLookupByPos(CUR_HBT, &btpos, (KEY) szKeyTemp,
NULL);
RcIndexFromKeyHbt(CUR_HBT, CUR_HMAP,
(QL) &pSrchClass->dwTemp, (KEY) szKeyTemp);
/*
* If the keyword we looked for is not a prefix of
* the string at btpos, then we are positioned at the
* keyword that would follow this keyword if it were
* in fact in the btree. Back up one keyword to let
* him see the previous one to give enough context so
* he sees his is not present. If already at the first
* keyword, don't back up any farther.
*/
if (!FIsPrefix(CUR_HBT, (KEY) pSrchClass->szKeyword,
(KEY) (LPSTR) szKeyTemp)) {
if (pSrchClass->dwTemp > 0)
pSrchClass->dwTemp--;
}
}
// If we are already at this topic, do nothing.
if (pSrchClass->dwTemp != (DWORD) pSrchClass->dwTop) {
pSrchClass->dwTop = pSrchClass->dwTemp;
SendMessage(hwndListBox, LB_SETTOPINDEX,
(WPARAM) (pSrchClass->dwTop == 0 ? 0 : pSrchClass->dwTop - 1), 0);
SendMessage(hwndListBox, LB_SETCURSEL, (WPARAM) pSrchClass->dwTop, 0);
}
}
break;
case IDOK:
switch (HIWORD(wParam)) {
case BN_CLICKED:
// Save the current keyword
if ((i = SendDlgItemMessage(hwndDlg, DLGVLISTBOX,
LB_GETCURSEL, 0, 0L)) != LB_ERR) {
GetDlgItemText(hwndDlg, DLGEDIT, (LPSTR) szKeyTemp,
MAXKEYLEN);
RcKeyFromIndexHbt(CUR_HBT, CUR_HMAP,
(KEY) (LPSTR) pSrchClass->szKeyword, i);
if (WCmpniSz(pSrchClass->szKeyword, szKeyTemp,
strlen(szKeyTemp)) != 0) {
MessageBox(hwndDlg, GetStringResource(wERRS_NOMATCH),
pszCaption, MB_OK | MB_ICONINFORMATION);
break;
}
}
else
break; // REVIEW: we should have an error message
HDE hde;
hde = HdeGetEnv();
if (IssGetSizeHss(pSrchClass->FindTopicTitles(hde,
pSrchClass->szKeyword)) > 1)
i = DialogBox(hInsNow, MAKEINTRESOURCE(IDDLG_TOPICS),
hwndDlg, (DLGPROC) TopicsDlg);
else
i = 0;
if (i != RETRY) {
strcpy(szSavedKeyword, pSrchClass->szKeyword); // save the keyword
PostMessage(GetParent(hwndDlg), WM_COMMAND,
IDDOSEARCH, i + 1);
}
break;
}
break;
case DLGVLISTBOX: // The Keyword listbox
switch (HIWORD(wParam)) {
case LBN_SELCHANGE:
// A new list item has been selected. Update the editbox!
ASSERT(GetDlgItem(hwndDlg, DLGVLISTBOX));
if ((pSrchClass->dwTemp = SendMessage(GetDlgItem(hwndDlg,
DLGVLISTBOX), LB_GETCURSEL, 0, 0L)) != (DWORD) LB_ERR) {
RcKeyFromIndexHbt(CUR_HBT, CUR_HMAP,
(KEY) (LPSTR) szKeyTemp, pSrchClass->dwTemp);
pSrchClass->fSelectionChange = TRUE;
SetDlgItemText(hwndDlg, DLGEDIT, szKeyTemp);
pSrchClass->fSelectionChange = FALSE;
pSrchClass->dwTop = pSrchClass->dwTemp;
}
break;
case LBN_DBLCLK:
PostMessage(hwndDlg, WM_COMMAND, MAKELONG(IDOK, BN_CLICKED), 0);
break;
}
break;
case IDCANCEL:
SendMessage(GetParent(hwndDlg), WM_COMMAND,
IDCANCEL, 0);
break;
}
break;
default:
return (FALSE);
} // switch
return (FALSE);
}
/***************************************************************************
FUNCTION: InitIndexDlg
PURPOSE: Initialize keyword list box
PARAMETERS:
hwndDlg
RETURNS:
COMMENTS:
REVIEW: we need to figure out what to do if there are no keywords,
or the help file cannot be opened.
MODIFICATION DATES:
27-May-1993 [ralphw]
***************************************************************************/
BOOL STDCALL CSearch::InitIndexDlg(HWND hwndDlg)
{
HWND hwndListBox = GetDlgItem(hwndDlg, DLGVLISTBOX);
// if we were already initialized, clear and re-initialize
if (hmapbt) {
FreeKeywordList();
if (hss)
FreeGh(hss);
}
ASSERT(cntFlags.fUseGlobalIndex);
if (cntFlags.fUseGlobalIndex) {
if (!hmapbtGid)
hmapbtGid = HmapbtOpenHfs(hfsGid, (LPCSTR)txtMAPNAME);
if (hmapbtGid) {
// Note that we only allow 'K' keywords
if (!hbtGid)
hbtGid = HbtOpenBtreeSz(txtKEYWORDBTREE, hfsGid,
fFSOpenReadOnly);
if (hbtGid) {
RcGetBtreeInfo(hbtGid, NULL, (QL) &cItems, NULL);
goto InitializeListbox;
}
else {
FreeGh(hmapbtGid);
hmapbtGid = NULL;
}
}
// REVIEW: This is really bad. Means we can't use a global index.
// Should we really try to continue?
cntFlags.fUseGlobalIndex = FALSE;
ASSERT(cntFlags.fUseGlobalIndex);
}
if (!hmapbt) {
if (!InitCurKeywords())
return FALSE;
}
InitializeListbox:
// If Win32s (1.3) doesn't support LB_SETCOUNT with 30,000+ items,
// then we'll need to roll our own listbox again
SendMessage(hwndListBox, LB_SETCOUNT, (WPARAM) pSrchClass->cItems, 0);
// Set initial focus and enable states
HWND hwndEdit = GetDlgItem(hwndDlg, DLGEDIT);
SetWindowText(hwndEdit, pSrchClass->szKeyword);
SendMessage(hwndEdit, EM_LIMITTEXT, MAXKEYLEN, 0L);
#ifndef TRUNCATED_KEYWORD
// REVIEW: is this really needed? Seems like it gets selected automatically
SendMessage(hwndEdit, EM_SETSEL, 0, -1); // select everything
if (GetWindowTextLength(hwndEdit) == 0)
SendMessage(hwndListBox, LB_SETCURSEL, 0,0);
#else
// Move cursor to the end of the string
SendMessage(hwndEdit, EM_SETSEL, strlen(pSrchClass->szKeyword), -1);
if (GetWindowTextLength(hwndEdit) == 0)
SendMessage(hwndListBox, LB_SETCURSEL, 0,0);
#endif
return TRUE;
}
/***************************************************************************
FUNCTION: CSearch::InitCurKeywords
PURPOSE: Initialize keywords from current help file
PARAMETERS:
void
RETURNS:
COMMENTS:
MODIFICATION DATES:
31-May-1993 [ralphw]
***************************************************************************/
BOOL STDCALL CSearch::InitCurKeywords(void)
{
QDE qde = QdeFromGh(HdeGetEnv());
hmapbt = HmapbtOpenHfs(QDE_HFS(qde), (LPCSTR)txtMAPNAME);
if (hmapbt == NULL)
return FALSE;
/*
* Open the keyword btree for use by keyword listbox, and see how
* many keywords are stored in it.
*/
hbt = HbtKeywordOpenHde(HdeGetEnv(), chBtreePrefixDefault);
if (hbt != NULL)
RcGetBtreeInfo(hbt, NULL, (QL) &cItems, NULL);
else {
return FALSE;
}
return TRUE;
}
/***************************************************************************
FUNCTION: CSearch::BadHelpFile
PURPOSE: Let the user know that we have a bad help file.
PARAMETERS:
qde
fm
RETURNS:
COMMENTS:
MODIFICATION DATES:
31-May-1993 [ralphw]
***************************************************************************/
void STDCALL CSearch::BadHelpFile(QDE qde, FM fm)
{
char szFName[MAX_PATH];
FreeKeywordList();
if (fm != NULL)
lstrcpy(szFName, PszFromGh(fm));
else if (qde != NULL)
lstrcpy(szFName, PszFromGh(QDE_FM(qde)));
else
return;
ErrorVarArgs(wERRS_BADFILE, wERRA_RETURN, szFName);
}
BOOL STDCALL CSearch::FFillTopicBox(HDE hde, HSS hss, HWND hwnd)
{
return FillTopicBox(hde, hss, hwnd, hfsMaster, szKeyword);
}
static BOOL STDCALL FillTopicBox(HDE hde, HSS hss, HWND hwnd, HFS hfsMaster, PSTR szKeyword)
{
HWND hwndLB = GetDlgItem(hwnd, DLGTOPICS);
int cbMax = 0;
HBT hbtRose = NULL;
HFS hfs;
ASSERT(hss != NULL);
ISS issTotal = IssGetSizeHss(hss);
HBT hbtTitle;
if (hfsGid && cntFlags.fUseGlobalIndex)
hfs = hfsGid;
else if (hde)
hfs = QDE_HFS(QdeFromGh(hde));
else
hfs = hfsMaster;
if (hfs != hfsGid) {
hbtTitle = HbtOpenBtreeSz(txtTTLBTREENAME, hfs, fFSOpenReadOnly);
if (hbtTitle == NULL) {
PutUpSearchErrorBox(hwnd, RcGetBtreeError());
return FALSE;
}
}
else
hbtTitle = NULL;
SendMessage(hwndLB, WM_SETREDRAW, FALSE, 0L); // prevent redrawing
HDC hdc = GetDC(hwndLB);
HFONT hfontOld = (HFONT) SelectObject(hdc, hfontDefault);
int iFile = -1;
HFS hfsKeyword = NULL;
HBT hbtKeywordTitle = NULL;
MASTER_RECKW* prec;
BOOL fKeyMacro = FALSE;
if (hfsGid && cntFlags.fUseGlobalIndex)
prec = HssToReckw(hss);
for (ISS iss = 0; iss < issTotal; iss++) {
char buffer[MAXKEYLEN + 10]; // add room for MASTER_TITLE_RECORD
MASTER_TITLE_RECORD mtr;
mtr.idHelpFile = (UINT) -1;
ASSERT(hfsGid);
ASSERT(cntFlags.fUseGlobalIndex);
if (hfsGid && cntFlags.fUseGlobalIndex) {
// copying mtr is easier for the logic at the end of the above
// if block
CopyMemory(&mtr, &prec->mtr[iss].idHelpFile, sizeof(MASTER_TITLE_RECORD));
if (mtr.idHelpFile == -1) {
if (!hbtRose) {
ASSERT(hfs);
hbtRose = HbtOpenBtreeSz(txtRose, hfs, fFSOpenReadOnly);
}
if (!hbtRose)
wsprintf(buffer, GetStringResource(sidUntitled), iss);
else if (!(RcLookupByKey(hbtRose,
(KEY) szKeyword, NULL, buffer)) ==
rcSuccess) {
wsprintf(buffer, GetStringResource(sidUntitled), iss);
}
}
else {
#ifdef _DEBUG
PSTR pszKeywordFile = pTblFiles->GetPointer(mtr.idHelpFile);
#endif
if ((int) mtr.idHelpFile != iFile ||
fKeyMacro != (mtr.addr == -1)) {
if (hbtKeywordTitle) {
RcCloseBtreeHbt(hbtKeywordTitle);
hbtKeywordTitle = NULL;
}
if (hfsKeyword)
RcCloseHfs(hfsKeyword);
hfsKeyword =
HfsOpenFm(pTblFiles->GetPointer(mtr.idHelpFile), fFSOpenReadOnly);
if (!hfsKeyword)
continue;
hbtKeywordTitle = HbtOpenBtreeSz(
(mtr.addr == -1) ? txtRose : txtTTLBTREENAME,
hfsKeyword, fFSOpenReadOnly);
if (!hbtKeywordTitle) {
RcCloseHfs(hfsKeyword);
hfsKeyword = NULL;
continue;
}
iFile = mtr.idHelpFile;
}
BTPOS btpos;
HASH hash;
if (mtr.addr == -1)
hash = HashFromSz(szKeyword);
RC rc = RcLookupByKey(hbtKeywordTitle,
(mtr.addr == -1) ? (KEY) (void*) &hash : (KEY) &mtr.addr,
&btpos, buffer);
if (rc == rcNoExists) {
// Deal with case where we are in between keys in a btree
if (FValidPos(&btpos)) {
LONG lBogus;
BTPOS btposNew;
rc = RcOffsetPos(hbtKeywordTitle, &btpos, (LONG) -1,
(QL) &lBogus, &btposNew);
if (rc == rcSuccess)
rc = RcLookupByPos(hbtKeywordTitle, &btposNew,
(KEY) (QL) &lBogus, buffer);
}
else
rc = RcLastHbt(hbtKeywordTitle, 0, buffer, NULL);
}
if (rc != rcSuccess) {
*buffer = '\0';
}
// Keyword macros store double strings for the title --
// first the macro itself, then a NULL, then the title.
else if (mtr.addr == -1)
strcpy(buffer, buffer + strlen(buffer) + 1);
}
}
else
RcGetTitleTextHss(hss, hbtTitle, iss, buffer, hfs, &hbtRose, szKeyword);
fKeyMacro = (mtr.addr == -1);
if (!*buffer)
wsprintf(buffer, GetStringResource(sidUntitled), iss);
#ifdef _DEBUG
PSTR pszHelpTitle;
if (mtr.idHelpFile != -1)
pszHelpTitle = pTblFiles->GetPointer(mtr.idHelpFile - 1);
#endif
if (mtr.idHelpFile != -1 &&
SendMessage(hwndLB, LB_FINDSTRING, (WPARAM) -1,
(LPARAM) buffer) != LB_ERR &&
*(pTblFiles->GetPointer(mtr.idHelpFile - 1))) {
strcat(buffer, " (");
lstrcat(buffer, pTblFiles->GetPointer(mtr.idHelpFile - 1));
strcat(buffer, ")");
}
// Associate with each item its unsorted index number:
int iSorted = SendMessage(hwndLB, LB_ADDSTRING, 0, (LPARAM) buffer);
SIZE sSize;
GetTextExtentPoint32(hdc, buffer, strlen(buffer), &sSize);
int cbCur = sSize.cx + LISTBOX_PAD;
if (cbCur > cbMax)
cbMax = cbCur;
// REVIEW: should we warn the user if we have an error?
if (iSorted == LB_ERR || iSorted == LB_ERRSPACE)
break; // probably out of memory
SendMessage(hwndLB, LB_SETITEMDATA, iSorted, (LPARAM) iss);
}
SelectObject(hdc, hfontOld);
ReleaseDC(hwndLB, hdc);
ResizeTopicsDialog(hwnd, hwndLB, cbMax);
InvalidateRect(hwndLB, NULL, TRUE);
SendMessage(hwndLB, WM_SETREDRAW, TRUE, 0L); // allow redrawing
if (hbtRose)
RcCloseBtreeHbt(hbtRose);
if (hbtTitle)
RcCloseBtreeHbt(hbtTitle);
PostMessage(hwndLB, LB_SETCURSEL, 0, 0L);
if (hbtKeywordTitle)
RcCloseBtreeHbt(hbtKeywordTitle);
if (hfsKeyword)
RcCloseHfs(hfsKeyword);
return TRUE;
}
/***************************************************************************
*
- Name:
-
* Purpose:
*
* Arguments:
*
* Returns:
*
***************************************************************************/
static void STDCALL PutUpSearchErrorBox(HWND hwnd, RC rc)
{
switch (rc) {
case rcOutOfMemory:
ErrorHwnd(hwnd, wERRS_OOM, wERRA_RETURN, wERRS_OOM);
break;
default:
ErrorHwnd(hwnd, wERRS_FSReadWrite, wERRA_RETURN, wERRS_FSReadWrite);
}
}
/***************************************************************************
FUNCTION: FindTopicTitles
PURPOSE: Find all the topic titles matching the supplied keyword
PARAMETERS:
hde
pszKeyword
RETURNS:
COMMENTS:
MODIFICATION DATES:
02-Feb-1993 [ralphw]
***************************************************************************/
HSS STDCALL CSearch::FindTopicTitles(HDE hde, LPCSTR pszKeyword)
{
HSS hssNew = HssSearchHde(hde, CUR_HBT, pszKeyword, chBtreePrefixDefault,
hfsMaster); // hfsMaster ignored if fUseGlobalIndex == TRUE
// Currently, a search should never fail here
if (RcGetSearchError() == rcSuccess) {
if (hde) {
QDE qde = (QDE) QdeFromGh(hde);
if (qde->hss != NULL)
FreeGh(qde->hss); // free previous search
qde->hss = hssNew;
}
else {
if (hss)
FreeGh(hss); // free previous search
hss = hssNew;
}
}
return hssNew;
}
/***************************************************************************
FUNCTION: TopicsDlg
PURPOSE: Displays all topics matching the current keyword
PARAMETERS:
hwndDlg
msg
p1
p2
RETURNS:
COMMENTS:
Assumes that szKeyword contains the current requested keyword
MODIFICATION DATES:
06-Apr-1993 [ralphw]
***************************************************************************/
DLGRET TopicsDlg(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
ChangeDlgFont(hwndDlg);
ASSERT(hfontDefault);
SendMessage(GetDlgItem(hwndDlg, DLGTOPICS), WM_SETFONT,
(WPARAM) hfontDefault, FALSE);
if (pSrchClass) {
HDE hde = HdeGetEnv();
// REVIEW: should we pay attention to result from FFillTopicBox?
pSrchClass->FFillTopicBox(hde,
pSrchClass->FindTopicTitles(hde, pSrchClass->szKeyword),
hwndDlg);
}
else {
HSS hss = (HSS) ptblLinks->GetIndex(1);
HDE hde = GetMacroHde();
FillTopicBox(GetMacroHde(), hss, hwndDlg, NULL,
ptblLinks->GetPointer(2));
}
return TRUE;
case WM_HELP:
OnF1Help(lParam, aKeywordIds);
return TRUE;
case WM_CONTEXTMENU:
OnContextMenu(wParam, aKeywordIds);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDCANCEL:
EndDialog(hwndDlg, RETRY);
break;
case IDOK:
/*
* Retrieve the unsorted index number that was set
* when the item was inserted:
*/
{
int i = SendDlgItemMessage(hwndDlg, DLGTOPICS,
LB_GETCURSEL, 0, 0);
// REVIEW: what to do on error
if (i != LB_ERR) {
i = SendDlgItemMessage(hwndDlg, DLGTOPICS,
LB_GETITEMDATA, i, 0L);
EndDialog(hwndDlg, i);
}
}
break;
case DLGTOPICS: // the topic listbox
switch(HIWORD(wParam)) {
case LBN_DBLCLK:
PostMessage(hwndDlg, WM_COMMAND, IDOK, 0);
break;
}
break;
}
}
return FALSE;
}
/***************************************************************************
FUNCTION: CSearch::FreeKeywordList
PURPOSE: Free all objects related to the current keyword list.
PARAMETERS:
void
RETURNS:
COMMENTS:
MODIFICATION DATES:
27-May-1993 [ralphw]
***************************************************************************/
void STDCALL CSearch::FreeKeywordList(void)
{
if (hss)
FreeGh(hss);
if (hfsMaster)
RcCloseHfs(hfsMaster);
if (hmapbt)
FreeGh(hmapbt);
if (hbt)
RcCloseBtreeHbt(hbt);
hss = hfsMaster = hmapbt = hbt = NULL;
}
/***************************************************************************
FUNCTION: doAlink
PURPOSE: Collect associative linked topics into a dialog box.
PARAMETERS:
psz -- associative link keyword
RETURNS:
COMMENTS:
MODIFICATION DATES:
19-Oct-1993 [ralphw]
***************************************************************************/
// REVIEW: need to pass in a context string to jump to in case of failure
extern "C" BOOL STDCALL doAlink(PSTR pszLinkWords, UINT flags,
PSTR pszContext, char chPrefix, PSTR pszWindow)
{
if (!hfsGid) {
if (HdeGetEnv() != NULL)
FindGidFile(GetCurFilename(),FALSE,0);
else
FindGidFile(fmCaller,FALSE,0);
}
ASSERT(hfsGid);
if (ptblLinks) {
delete ptblLinks;
}
ptblLinks = new CTable();
LookForAlinks(pszLinkWords, chPrefix, flags);
if (ptblLinks->CountStrings() < 1) {
if (flags & AFLAG_CHECK_FOR_MATCH) {
ReportAlinkResult(FALSE, chPrefix, pszLinkWords);
goto Cleanup;
}
if (!IsEmptyString(pszContext))
MacroErrorPopup(pszContext);
else if (!(flags & AFLAG_NO_FAIL_CLOSE))
Error(wERRS_NO_ALINK, wERRA_RETURN);
Cleanup:
delete ptblLinks;
ptblLinks = NULL;
if (!(flags & AFLAG_NO_FAIL_CLOSE) && AreAnyWindowsVisible(0) < 0)
CloseHelp();
return FALSE; // REVIEW: do something intelligent
}
if (flags & AFLAG_CHECK_FOR_MATCH) {
ReportAlinkResult(TRUE, chPrefix, pszLinkWords);
return TRUE;
}
BOOL fResult;
ASSERT(cntFlags.fUseGlobalIndex);
if ((flags & AFLAG_INDEX_ONLY || pTblFiles->CountStrings() < 3) &&
chPrefix == 'K' && cntFlags.fUseGlobalIndex) {
HSS hss = (HSS) ptblLinks->GetIndex(1);
HDE hde = GetMacroHde();
ASSERT(hde);
int i;
if (IssGetSizeHss(hss) > 1)
i = DialogBox(hInsNow, MAKEINTRESOURCE(IDDLG_TOPICS),
ahwnd[iCurWindow].hwndParent, (DLGPROC) TopicsDlg);
else
i = 0;
if (i == RETRY) {
FreeGh(hss); // free previous search
return FALSE;
}
QDE qde = (QDE) QdeFromGh(hde);
if (qde->hss != NULL)
FreeGh(qde->hss); // free previous search
qde->hss = hss;
CompleteSearch(i + 1, (!hfsGid || !cntFlags.fUseGlobalIndex));
return TRUE;
}
else if (ptblLinks->CountStrings() < 3 && (flags & AFLAG_JUMP_ON_SINGLE))
fResult = doAlinkJump(1, pszWindow, chPrefix);
else
fResult = doAlinkJump(CallDialog(IDDLG_TOPICS,
ahwnd[iCurWindow].hwndParent, ALinkDlg), pszWindow, chPrefix);
if (!fResult && !(flags & AFLAG_NO_FAIL_CLOSE) &&
AreAnyWindowsVisible(0) < 0)
CloseHelp();
return fResult;
}
/***************************************************************************
FUNCTION: LookForAlinks
PURPOSE: Look for associative links in all files in the pTblFile table
PARAMETERS:
pszLinkWords
RETURNS:
COMMENTS:
MODIFICATION DATES:
25-Oct-1993 [ralphw]
***************************************************************************/
INLINE static void STDCALL LookForAlinks(PSTR pszLinkWords,
char chPrefix, UINT flags)
{
FM fm = NULL;
char szBtreeName[20];
strcpy(szBtreeName, txtKEYWORDBTREE);
szBtreeName[1] = chPrefix;
CStr cszWords(pszLinkWords);
QDE qde = GetMacroHde();
if (!qde)
return;
/*
* We really, really, really want the pTblFiles table. Without a .GID
* file, it might not exist, so we create one and just add the current
* filename as a link file.
*/
if (!pTblFiles) {
pTblFiles = new CTable();
if (!pFileInfo)
pFileInfo = (GID_FILE_INFO*) GhAlloc(GMEM_FIXED,
sizeof(GID_FILE_INFO));
pFileInfo[0].filetype = CHFLAG_LINK;
// Two entries per file: title, filename
pTblFiles->AddString(QDE_RGCHTITLE(qde));
pTblFiles->AddString(GetCurFilename()); // Add the filename
}
/*
* If AFLAG_INDEX_ONLY is set, we're looking for K keywords, there's a
* global index and we only have one file, then this call was made to
* deal with sort problems with 3.1 DBCS files (and Russian, Czech,
* Hungarian, etc.). We will use the .GID file instead of the help file's
* index, since the .GID file will be the only one to have keywords
* sorted in a manner that we can understand. Note that while this can
* accept multiple semi-colon separated keywords, once one of the
* keywords matches, all the others are tossed.
*/
if ((flags & AFLAG_INDEX_ONLY || pTblFiles->CountStrings() < 3) &&
chPrefix == 'K' && cntFlags.fUseGlobalIndex) {
ASSERT(hfsGid);
HBT hbt = HbtOpenBtreeSz(szBtreeName, hfsGid, fFSOpenReadOnly);
if (!hbt) {
return;
}
PSTR psz = FirstNonSpace(cszWords.psz);
for(;;) {
PSTR pszSemi = StrChrDBCS(psz, ';');
if (pszSemi)
*pszSemi = '\0';
RemoveTrailingSpaces(psz);
BTPOS btpos;
if (RcLookupByKey(hbt, (KEY) (LPSTR) psz, &btpos, NULL) ==
rcSuccess) {
HSS hss = HssSearchHde(NULL, hbt, psz, chPrefix, NULL);
if (!hss)
continue; // theoretically impossible
ISS issTotal = IssGetSizeHss(hss);
ptblLinks->AddIndexHitString((UINT) hss, issTotal, txtZeroLength);
ptblLinks->AddString(psz);
RcCloseBtreeHbt(hbt);
return; // return on the first match
}
if (pszSemi) {
*pszSemi = ';'; // MUST restore the semi-colon!
psz = FirstNonSpace(pszSemi + 1);
}
else
break;
}
RcCloseBtreeHbt(hbt);
return;
}
/*
* These may change as we scan through various help files, so we save
* the current values here and restore them when we leave.
*/
LCID lcidSave = lcid;
KEYWORD_LOCALE kwSave = qde->pdb->kwlcid;
UINT err;
char szCurTitle[260];
GetCurrentTitleQde(qde, szCurTitle, sizeof(szCurTitle));
/*
* We start with 2 because pTblFiles is a double-table, with the
* first string containing the help title, and the second string
* containing the help file name.
*/
/*
* Note that we don't normally use the .GID file. This will make this
* loop slightly slower since we must open every file in the ptblFiles
* table, however it saves us a bunch of conditional code on whether we
* have and can use the .GID file.
*/
for (int index = 2; index <= pTblFiles->CountStrings(); index += 2) {
if (pFileInfo[index / 2 - 1].filetype & CHFLAG_MISSING)
continue;
if (flags & AFLAG_INDEX_ONLY &&
pFileInfo[index / 2 - 1].filetype & CHFLAG_LINK)
continue;
CFM fm(pTblFiles->GetPointer(index));
// This will fail if we run out of memory
if (!fm.fm)
continue; // happens when :link in .CNT is to missing file
HFS hfs;
if (!(hfs = HfsOpenFm(fm.fm, fFSOpenReadOnly)))
continue; // just try the next file
FReadSystemFile(hfs, NULL, &err, tagLCID);
HBT hbt = HbtOpenBtreeSz(szBtreeName, hfs, fFSOpenReadOnly);
if (!hbt) {
RcCloseHfs(hfs);
continue;
}
PSTR psz = FirstNonSpace(cszWords.psz);
BTPOS btpos;
PSTR pszSemi;
for(;;) {
pszSemi = StrChrDBCS(psz, ';');
if (pszSemi)
*pszSemi = '\0';
RemoveTrailingSpaces(psz);
if (RcLookupByKey(hbt, (KEY) (LPSTR) psz, &btpos, NULL) ==
rcSuccess) {
/*
* We have a match, however, we need to fool HssSearchHde
* into thinking we're dealing with just a single help file,
* so we temporarily zero-out the hfsGid handle long enough
* for HssSearchHde to do it's thing.
*/
HFS hfsSaveGid = hfsGid;
hfsGid = NULL;
HSS hss = HssSearchHde(NULL, hbt, psz,
chPrefix, hfs);
hfsGid = hfsSaveGid;
if (!hss)
goto NextItem;
HBT hbtTitle = HbtOpenBtreeSz(txtTTLBTREENAME, hfs,
fFSOpenReadOnly);
if (!hbtTitle) {
FreeGh(hss);
break;
}
ISS issTotal = IssGetSizeHss(hss);
for (ISS iss = 0; iss < issTotal; iss++) {
char buffer[MAXKEYLEN + MAX_PATH];
int i;
// Add the index of the file and the title to our table
RcGetTitleTextHss(hss, hbtTitle, iss, buffer, NULL, NULL, NULL);
if (!(flags & AFLAG_CHECK_FOR_MATCH) &&
strcmp(buffer, szCurTitle) == 0 &&
FSameFile(HdeGetEnv(), pTblFiles->GetPointer(index)))
continue;
if (*pTblFiles->GetPointer(index - 1)) {
if (flags & AFLAG_INCLUDE_TITLES) {
strcat(buffer, " (");
lstrcat(buffer, pTblFiles->GetPointer(index - 1));
strcat(buffer, ")");
}
else {
/*
* BUGBUG: this only adds the title to the
* second occurence, but leaves the first blank.
* Of course, we probably no longer know what help
* file we were in. One possible solution would be
* to always include the title, but separated with
* a NULL. We could then save off which ones are
* duplicated, and then make one pass through to
* remove the null for all duplicate titles.
*/
for (i = 1; i <= ptblLinks->CountStrings(); i += 2) {
if (WCmpiSz(buffer,
ptblLinks->GetPointer(i) +
sizeof(UINT) * 2) == 0) {
strcat(buffer, " (");
lstrcat(buffer, pTblFiles->GetPointer(index - 1));
strcat(buffer, ")");
break;
}
}
}
}
ptblLinks->AddIndexHitString(index, iss, buffer);
ptblLinks->AddString(psz);
}
RcCloseBtreeHbt(hbtTitle);
FreeGh(hss);
}
NextItem:
if (pszSemi) {
*pszSemi = ';'; // MUST restore the semi-colon!
psz = FirstNonSpace(pszSemi + 1);
}
else
break;
}
RcCloseBtreeHbt(hbt);
RcCloseHfs(hfs);
}
lcid = lcidSave;
qde->pdb->kwlcid = kwSave;
}
DLGRET ALinkDlg(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
int i;
PSTR pszSep;
HWND hwndLB;
char szBuf[400];
HDC hdc;
int cbMax;
SIZE sSize;
HFONT hfontOld;
switch(msg) {
case WM_INITDIALOG:
ChangeDlgFont(hwndDlg);
hwndLB = GetDlgItem(hwndDlg, DLGTOPICS);
ASSERT(hfontDefault);
SendMessage(hwndLB, WM_SETFONT, (WPARAM) hfontDefault, FALSE);
pszSep = GetStringResource(sidAlinkSep);
cbMax = 0;
hdc = GetDC(hwndLB);
ASSERT(hfontDefault);
hfontOld = (HFONT)SelectObject(hdc, hfontDefault);
for (i = 1; i <= (int) ptblLinks->CountStrings(); i += 2) {
int iSorted;
int cbCur;
lstrcpy(szBuf, ptblLinks->GetIHPointer(i));
// Associate with each item its unsorted index number:
iSorted = SendMessage(hwndLB, LB_ADDSTRING, 0,
(LPARAM) (LPSTR) szBuf);
// BUGBUG: should use GetTextWidth or whatever it is
GetTextExtentPoint32(hdc, szBuf, strlen(szBuf),(LPSIZE)&sSize);
cbCur = sSize.cx + LISTBOX_PAD; // include some padding
if (cbCur > cbMax)
cbMax = cbCur;
// REVIEW: should we warn the user if we have an error?
if (iSorted == LB_ERR || iSorted == LB_ERRSPACE)
break; // probably out of memory
SendMessage(hwndLB, LB_SETITEMDATA, iSorted, (LPARAM) i);
}
SelectObject(hdc, hfontOld);
SendMessage(hwndLB, LB_SETCURSEL, 0, 0);
ReleaseDC(hwndLB, hdc);
ResizeTopicsDialog(hwndDlg, hwndLB, cbMax);
break;
case WM_HELP:
OnF1Help(lParam, aKeywordIds);
return TRUE;
case WM_CONTEXTMENU: // user right-clicked something
OnContextMenu(wParam, aKeywordIds);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDCANCEL:
EndDialog(hwndDlg, 0);
break;
case IDOK:
/*
* Retrieve the unsorted index number that was set
* when the item was inserted:
*/
i = SendDlgItemMessage(hwndDlg, DLGTOPICS,
LB_GETCURSEL, 0, 0);
// REVIEW: what to do on error
if (i != LB_ERR) {
i = SendDlgItemMessage(hwndDlg, DLGTOPICS,
LB_GETITEMDATA, i, 0L);
EndDialog(hwndDlg, i);
}
break;
case DLGTOPICS: // the topic listbox
switch(HIWORD(wParam)) {
case LBN_DBLCLK:
PostMessage(hwndDlg, WM_COMMAND, IDOK, 0);
break;
}
break;
}
break;
}
return FALSE;
}
/***************************************************************************
FUNCTION: doAlinkJump
PURPOSE: Perform the actual jump, switching help files if necessary
PARAMETERS:
DlgResult
RETURNS:
COMMENTS:
MODIFICATION DATES:
27-Oct-1993 [ralphw]
***************************************************************************/
static BOOL STDCALL doAlinkJump(int DlgResult, PSTR pszWindow, char chPrefix)
{
if (DlgResult <= 0)
return FALSE; // REVIEW: should we tell the user something useful?
// Do we need to change help files?
if (!IsCurrentFile(pTblFiles->GetPointer(ptblLinks->GetIndex(DlgResult)))) {
// REVIEW: do we need to change curIndex?
ASSERT(pTblFiles);
FM fm = FmNew(pTblFiles->GetPointer(ptblLinks->GetIndex(DlgResult)));
fDelayShow = TRUE;
// BUGBUG: if we jump to a topic with an ALink test, ptblLinks will get
// deleted.
if (!FReplaceHde(IsEmptyString(pszWindow) ? txtMain : pszWindow, &fm, NULL)) {
RemoveFM(&fm);
return FALSE;
}
fDelayShow = FALSE;
ASSERT(!fm);
}
HDE hde = GetMacroHde();
HSS hssNew;
HBT hbt = HbtKeywordOpenHde(hde, chPrefix);
// This should never happen since we already opened the table once
if (hbt == NULL) {
PostErrorMessage(wERRS_NOSRCHINFO);
return FALSE;
}
if (!IsEmptyString(pszWindow))
FFocusSzHde(pszWindow, hde, FALSE);
HFS hfsSaveGid = hfsGid;
hfsGid = NULL;
hssNew = HssSearchHde(hde, hbt, ptblLinks->GetPointer(DlgResult + 1),
chPrefix, NULL);
ASSERT(hssNew);
hfsGid = hfsSaveGid;
RcCloseBtreeHbt(hbt);
QDE qde = (QDE) QdeFromGh(hde);
if (qde->hss != NULL)
FreeGh(qde->hss); // free previous search
qde->hss = hssNew;
// BUGBUG: deal with macro keywords
LA la;
if (RcGetLAFromHss(hssNew, qde,
ptblLinks->GetHit(DlgResult), &la, NULL) == rcSuccess) {
if (IsEmptyString(pszWindow)) {
HBT hbtViola;
BOOL fWindowSet = FALSE;
if ((hbtViola = HbtOpenBtreeSz(txtViola, QDE_HFS(qde), fFSOpenReadOnly))) {
int iWindow;
if (RcLookupByKey(hbtViola, (KEY) &la.pa, NULL, &iWindow) == rcSuccess) {
#ifdef _DEBUG
char szBuf[256];
wsprintf(szBuf, "Window footnote: %s\r\n",
ConvertToWindowName(iWindow, qde));
SendStringToParent(szBuf);
#endif
fWindowSet = TRUE;
FFocusSzHde(ConvertToWindowName(iWindow, qde), (HDE) qde, FALSE);
}
RcCloseBtreeHbt(hbtViola);
}
if (!fWindowSet) {
PSTR psz;
if (pszHelpBase && (psz = StrChrDBCS(pszHelpBase,
WINDOWSEPARATOR))) {
if (IsWindowVisible(ahwnd[MAIN_HWND].hwndParent))
ShowWindow(ahwnd[MAIN_HWND].hwndParent, SW_HIDE);
FFocusSzHde(psz + 1, hde, FALSE);
}
else if (!IsWindowVisible(ahwnd[iCurWindow].hwndParent))
ShowWindow(ahwnd[iCurWindow].hwndParent,
IsIconic(ahwnd[iCurWindow].hwndParent) ? SW_RESTORE : SW_SHOW);
}
}
TopicGoto(fGOTO_LA, (LPVOID) &la);
}
return TRUE;
}
/***************************************************************************
FUNCTION: OnDrawItem
PURPOSE: Called to paint a list-box item
PARAMETERS:
lpdrws
RETURNS:
COMMENTS:
If the current and previous keyword contain a comma, semi-colon,
or colon, then only show the portion of the keyword that follows
that punctuation character.
MODIFICATION DATES:
07-Jul-1993 [ralphw]
***************************************************************************/
const char COMMA = ',';
const char SEMICOLON = ';';
const char COLON = ':';
#define ODA_CLEAR 0x0008
#ifndef NO_PRAGMAS
#pragma data_seg(".text", "CODE")
#endif
const char txt4Spaces[] = " ";
#ifndef NO_PRAGMAS
#pragma data_seg()
#endif
BOOL STDCALL CSearch::OnDrawItem(LPDRAWITEMSTRUCT lpdrws)
{
if (!hbt && !hbtGid) // do nothing if we don't have any keywords
return FALSE;
HDC hdc = lpdrws->hDC;
RECT rc = lpdrws->rcItem;
char szKeyTemp[MAXKEYLEN];
PSTR pszIndexSep;
if (lpdrws->itemAction & ODA_CLEAR)
*szKeyTemp = '\0';
else {
PSTR psz;
char szPrevKeyword[MAXKEYLEN];
BOOL fPrevIsIndex;
// Get previous keyword, and look for index separator
if (lpdrws->itemID > 0) {
pszIndexSep = pszIndexSeparators;
RcKeyFromIndexHbt(CUR_HBT, CUR_HMAP,
(KEY) (LPSTR) szPrevKeyword, (LONG) lpdrws->itemID - 1);
do {
if ((psz = StrChrDBCS(szPrevKeyword, *pszIndexSep))) {
if (*psz == ':' && psz[1] == ':')
psz++;
psz[1] = '\0';
break;
}
pszIndexSep++;
} while (*pszIndexSep);
}
else
szPrevKeyword[0] = '\0';
fPrevIsIndex = (BOOL) psz;
RcKeyFromIndexHbt(CUR_HBT, CUR_HMAP,
(KEY) (LPSTR) szKeyTemp, (LONG) lpdrws->itemID);
if (szPrevKeyword[0]) {
int cb;
/*
* If the current keyword contains an index separator, then
* save its position.
*/
if (!fPrevIsIndex) {
pszIndexSep = pszIndexSeparators;
do {
if ((psz = StrChrDBCS(szKeyTemp, *pszIndexSep))) {
if (*psz == ':' && psz[1] == ':')
psz++; // C++ class definition
cb = psz - szKeyTemp;
break;
}
pszIndexSep++;
} while (*pszIndexSep);
}
/*
* If the previous string is as long and equal to the current
* string up to the separator, then the current keyword is an
* index sub-entry. If the previous keyword contained an index
* separator and it matches up the the current keyword's
* separator, then the current keyword is an index sub-entry.
*/
if (!fPrevIsIndex && psz &&
lstrlen(szPrevKeyword) == cb &&
WCmpniSz(szKeyTemp, szPrevKeyword, cb - 1) == 0 &&
szKeyTemp[cb + 1]) {
lstrcpy(szPrevKeyword, (LPCSTR)txt4Spaces);
lstrcat(szPrevKeyword, FirstNonSpace(psz + 1));
lstrcpy(szKeyTemp, szPrevKeyword);
}
else if (fPrevIsIndex && WCmpniSz(szKeyTemp, szPrevKeyword,
lstrlen(szPrevKeyword)) == 0) {
/*
* Display just the portion of the keyword that appears
* after the index separator, preceded by four spaces.
*/
if (*FirstNonSpace(szKeyTemp + (psz - szPrevKeyword) + 1)) {
lstrcpy(szPrevKeyword, (LPCSTR)txt4Spaces);
lstrcat(szPrevKeyword, FirstNonSpace(szKeyTemp +
(psz - szPrevKeyword) + 1));
lstrcpy(szKeyTemp, szPrevKeyword);
}
}
/*
* Sometimes trailing punctuation is used to get correct
* sorting. We remove that trailing punctuation to make it look
* better.
*/
int cbLastChar = strlen(szKeyTemp) - 1;
if (StrChrDBCS(pszIndexSeparators, szKeyTemp[cbLastChar]))
szKeyTemp[cbLastChar] = '\0';
}
if (lpdrws->itemState & ODS_SELECTED) {
SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
}
else {
SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
}
}
#if defined(BIDI_MULT) // jgross
if (RtoL) {
DWORD ExtraStyle = 0;
char DummyBuffer1[MAXKEYLEN]; // for some reason, BiDiLayout
char DummyBuffer2[MAXKEYLEN]; // requires these, but I don't
BiDiLayout( szText, // lpszString
CbLenSz(szText), // cbString
(LPBYTE)&DummyBuffer1, // lpStrOut
NULL, // lpClassIn
(LPBYTE)&DummyBuffer2, // lpClassOut
NULL, // lpIOvect
NULL, // lpOIvect
(UINT FAR *)&ExtraStyle, // lpwFlagsOut
0); // wFlags
if ((ExtraStyle & BIDI_LATIN_ONLY) == BIDI_LATIN_ONLY)
ExtraStyle = 0;
else
ExtraStyle = ETO_RTL_READING;
ExtTextOut(hdc,
rect.right - 2 - LOWORD(GetTextExtent(hds,szText,CbLenSz(szText))),
rect.top,
ETO_OPAQUE | ExtraStyle,
&rect,
szText,
CbLenSz(szText),
(LPINT)0);
}
else
#endif
ExtTextOut(hdc, rc.left + 2, rc.top, ETO_OPAQUE, &rc,
szKeyTemp, strlen(szKeyTemp), NULL);
if (lpdrws->itemState & ODS_FOCUS)
DrawFocusRect(hdc, &rc);
if (lpdrws->itemState & ODS_SELECTED) {
SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
}
return TRUE;
}
static void STDCALL ResizeTopicsDialog(HWND hwndDlg, HWND hwndLB,
int cbMax)
{
RECT rcLB;
GetWindowRect(hwndLB, &rcLB);
cbMax += GetSystemMetrics(SM_CXVSCROLL); // assume a scroll bar
if (RECT_WIDTH(rcLB) < cbMax) {
int diff = cbMax - RECT_WIDTH(rcLB); // change cbMax into
RECT rcParent;
HWND hwndButton;
RECT rcButton;
int offset;
GetWindowRect(hwndDlg, &rcParent);
int pad = rcLB.left - rcParent.left;
if (rcParent.right + diff < cxScreen)
rcParent.right += diff;
else {
rcParent.left -= (diff / 2);
rcParent.right += (diff / 2);
if (rcParent.right > cxScreen) {
diff = rcParent.right - cxScreen;
rcParent.right = cxScreen;
rcParent.left -= diff;
}
if (rcParent.left < 0)
rcParent.left = 0;
}
MoveRectWindow(hwndDlg, &rcParent, TRUE);
rcLB.left = rcParent.left + pad;
rcLB.right = rcParent.right - pad;
MoveClientWindow(hwndDlg, hwndLB, &rcLB, FALSE);
// Reposition the Display and Cancel buttons relative to the
// new listbox position.
hwndButton = GetDlgItem(hwndDlg, IDCANCEL);
GetWindowRect(hwndButton, &rcButton);
offset = rcLB.right - rcButton.right;
rcButton.left += offset;
rcButton.right += offset;
MoveClientWindow(hwndDlg, hwndButton, &rcButton, FALSE);
hwndButton = GetDlgItem(hwndDlg, IDOK);
GetWindowRect(hwndButton, &rcButton);
rcButton.left += offset;
rcButton.right += offset;
MoveClientWindow(hwndDlg, hwndButton, &rcButton, FALSE);
}
}
LRESULT EXPORT EditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_KEYDOWN:
case WM_KEYUP:
if ( wParam == VK_UP ||
wParam == VK_DOWN ||
wParam == VK_PRIOR ||
wParam == VK_NEXT
) {
SendMessage(GetDlgItem(GetParent(hwnd), DLGVLISTBOX),
msg, wParam, lParam);
// Move caret to the end of the edit control
PostMessage(hwnd, msg, VK_END, lParam);
return 0;
}
// Intentionally fall through
default:
return CallWindowProc(lpfnlEditWndProc, hwnd, msg, wParam,
lParam);
}
}
#ifndef _CTABLE_INCLUDED
#include "inc\table.h"
#endif
const int TABLE_ALLOC_SIZE = 4096; // allocate in page increments
const int MAX_POINTERS = (1024 * 1024); // 1 meg, 260,000+ strings
const int MAX_STRINGS = (10 * 1024 * 1024) - 4096L; // 10 megs
// Align on 32 bits for Intel, 64 bits for MIPS
#ifdef _X86_
const int ALIGNMENT = 4;
#else
const int ALIGNMENT = 8;
#endif
/***************************************************************************
FUNCTION: CreateTable
PURPOSE: Creates a table with the specified initial size
COMMENTS:
MODIFICATION DATES:
11-Dec-1990 [ralphw]
***************************************************************************/
CTable::CTable()
{
InitializeTable();
}
/***************************************************************************
FUNCTION: =
PURPOSE: Copies a table -- only works with tables containing ONLY
strings. Won't work with tables that combined data with
the strings.
PARAMETERS:
tblSrc
RETURNS:
COMMENTS:
MODIFICATION DATES:
26-Mar-1994 [ralphw]
***************************************************************************/
const CTable& CTable::operator =(const CTable& tblSrc)
{
Empty();
int srcpos = 1;
while (srcpos < tblSrc.endpos) {
if (endpos >= maxpos)
IncreaseTableBuffer();
if ((ppszTable[endpos] =
TableMalloc(strlen(tblSrc.ppszTable[srcpos]) + 1)) == NULL) {
OOM();
return *this;
}
strcpy(ppszTable[endpos++], tblSrc.ppszTable[srcpos++]);
}
return *this;
}
/***************************************************************************
FUNCTION: ~CTable
PURPOSE: Close the table and free all memory associated with it
RETURNS:
COMMENTS:
MODIFICATION DATES:
26-Feb-1990 [ralphw]
27-Mar-1990 [ralphw]
Pass the address of the handle, so that we can set it to NULL.
This eliminates the chance of using a handle after it's memory
has been freed.
***************************************************************************/
CTable::~CTable(void)
{
Cleanup();
}
void STDCALL CTable::Cleanup(void)
{
if (pszBase) {
VirtualFree(pszBase, cbStrings, MEM_DECOMMIT);
VirtualFree(pszBase, 0, MEM_RELEASE);
}
if (ppszTable) {
VirtualFree(ppszTable, cbPointers, MEM_DECOMMIT);
VirtualFree(ppszTable, 0, MEM_RELEASE);
}
}
/***************************************************************************
FUNCTION: CTable::Empty
PURPOSE: Empties the current table by freeing all memory, then
recreating the table using the default size
PARAMETERS:
void
RETURNS:
COMMENTS:
MODIFICATION DATES:
22-Feb-1994 [ralphw]
***************************************************************************/
void STDCALL CTable::Empty(void)
{
Cleanup();
InitializeTable();
}
/***************************************************************************
FUNCTION: GetString
PURPOSE: get a line from the table
RETURNS: FALSE if there are no more lines
COMMENTS:
If no strings have been placed into the table, the return value
is FALSE.
MODIFICATION DATES:
01-Jan-1990 [ralphw]
***************************************************************************/
BOOL STDCALL CTable::GetString(PSTR pszDst)
{
*pszDst = 0; // clear the line no matter what happens
if (curpos >= endpos)
return FALSE;
strcpy(pszDst, (PCSTR) ppszTable[curpos++]);
return TRUE;
}
BOOL STDCALL CTable::GetString(PSTR pszDst, int pos)
{
*pszDst = 0; // clear the line no matter what happens
if (pos >= endpos || pos == 0)
return FALSE;
strcpy(pszDst, (PCSTR) ppszTable[pos]);
return TRUE;
}
BOOL STDCALL CTable::GetIntAndString(int* plVal, PSTR pszDst)
{
*pszDst = 0; // clear the line no matter what happens
if (curpos >= endpos)
return FALSE;
*plVal = *(int *) ppszTable[curpos];
strcpy(pszDst, (PCSTR) ppszTable[curpos++] + sizeof(int));
return TRUE;
}
/***************************************************************************
FUNCTION: AddString
PURPOSE: Add a string to a table
RETURNS:
COMMENTS:
MODIFICATION DATES:
01-Jan-1990 [ralphw]
***************************************************************************/
int STDCALL CTable::AddString(PCSTR pszString)
{
if (endpos >= maxpos)
IncreaseTableBuffer();
if ((ppszTable[endpos] =
TableMalloc(strlen(pszString) + 1)) == NULL)
return 0;
strcpy(ppszTable[endpos], pszString);
return endpos++;
}
int STDCALL CTable::AddData(int cb, const void* pdata)
{
if (endpos >= maxpos)
IncreaseTableBuffer();
if ((ppszTable[endpos] = TableMalloc(cb)) == NULL)
return 0;
CopyMemory(ppszTable[endpos], pdata, cb);
return endpos++;
}
int STDCALL CTable::AddIntAndString(int lVal, PCSTR pszString)
{
if (endpos >= maxpos)
IncreaseTableBuffer();
if ((ppszTable[endpos] =
TableMalloc(strlen(pszString) + 1 + sizeof(int))) == NULL)
return 0;
*(int*) ppszTable[endpos] = lVal;
strcpy(ppszTable[endpos] + sizeof(int), pszString);
return endpos++;
}
/***************************************************************************
FUNCTION: IncreaseTableBuffer
PURPOSE: Called when we need more room for string pointers
PARAMETERS:
RETURNS:
COMMENTS:
MODIFICATION DATES:
23-Feb-1992 [ralphw]
***************************************************************************/
void STDCALL CTable::IncreaseTableBuffer(void)
{
cbPointers += TABLE_ALLOC_SIZE;
maxpos = cbPointers / sizeof(PSTR);
if (!VirtualAlloc(ppszTable, cbPointers, MEM_COMMIT, PAGE_READWRITE)) {
OOM();
return;
}
}
/***************************************************************************
FUNCTION: TableMalloc
PURPOSE: Suballocate memory
RETURNS:
pointer to the memory
COMMENTS:
Instead of allocating memory for each string, memory is used from 4K
blocks. When the table is freed, all memory is freed as a single
unit. This has the advantage of speed for adding strings, speed for
freeing all strings, and low memory overhead to save strings.
MODIFICATION DATES:
26-Feb-1990 [ralphw]
26-Mar-1994 [ralphw]
Ported to 32-bits
***************************************************************************/
PSTR STDCALL CTable::TableMalloc(int cb)
{
/*
* Align allocation request so that all allocations fall on an
* alignment boundary (32 bits for Intel, 64 bits for MIPS).
*/
cb = (cb & (ALIGNMENT - 1)) ?
cb / ALIGNMENT * ALIGNMENT + ALIGNMENT : cb;
if (CurOffset + cb >= cbStrings) {
do {
cbStrings += TABLE_ALLOC_SIZE;
} while (CurOffset + cb >= cbStrings);
// We rely on VirtualAlloc to fail if cbStrings exceeds MAX_STRINGS
if (!VirtualAlloc(pszBase, cbStrings, MEM_COMMIT, PAGE_READWRITE)) {
OOM();
return NULL;
}
}
int offset = CurOffset;
CurOffset += cb;
return pszBase + offset;
}
/***************************************************************************
FUNCTION: SetPosition
PURPOSE: Sets the position for reading from the table
RETURNS:
COMMENTS:
MODIFICATION DATES:
26-Feb-1990 [ralphw]
16-Oct-1990 [ralphw]
If table position is to large, set to the end of the table,
not the last line.
***************************************************************************/
BOOL FASTCALL CTable::SetPosition(int pos)
{
if (pos >= endpos)
pos = endpos;
curpos = ((pos == 0) ? 1 : pos);
return TRUE;
}
/***************************************************************************
FUNCTION: IsStringInTable
PURPOSE: Determine if the string is already in the table
RETURNS: position if the string is already in the table,
0 if the string isn't found
COMMENTS:
The comparison is case-insensitive, and is considerably
slower then IsCSStringInTable
MODIFICATION DATES:
02-Mar-1990 [ralphw]
***************************************************************************/
int STDCALL CTable::IsStringInTable(PCSTR pszString)
{
int i;
if (!lcid) {
/*
* Skip over as many strings as we can by just checking the first
* letter. This avoids the overhead of the _strcmpi() function call.
*/
char chLower = tolower(*pszString);
char chUpper = toupper(*pszString);
for (i = 1; i < endpos; i++) {
if ((*ppszTable[i] == chLower || *ppszTable[i] == chUpper)
&& _strcmpi(ppszTable[i], pszString) == 0)
return i;
}
}
else { // Use NLS string comparison
for (i = 1; i < endpos; i++) {
if (CompareStringA(lcid, fsCompareI | NORM_IGNORECASE,
pszString, -1, ppszTable[i], -1) == 1)
return i;
}
}
return 0;
}
/***************************************************************************
FUNCTION: CTable::IsCSStringInTable
PURPOSE: Case-sensitive search for a string in a table
PARAMETERS:
pszString
RETURNS:
COMMENTS:
MODIFICATION DATES:
12-Jun-1994 [ralphw]
***************************************************************************/
int STDCALL CTable::IsCSStringInTable(PCSTR pszString)
{
char szBuf[sizeof(DWORD) + 1];
DWORD cmp;
if (strlen(pszString) < sizeof(DWORD)) {
ZeroMemory(szBuf, sizeof(DWORD) + 1);
strcpy(szBuf, pszString);
cmp = *(DWORD*) szBuf;
}
else
cmp = *(DWORD*) pszString;
for (int i = 1; i < endpos; i++) {
if (cmp == *(DWORD*) ppszTable[i] &&
strcmp(ppszTable[i], pszString) == 0)
return i;
}
return 0;
}
int STDCALL CTable::IsStringInTable(HASH hash, PCSTR pszString)
{
for (int i = 1; i < endpos; i++) {
if (hash == *(HASH *) ppszTable[i] &&
// this avoids the very rare hash collision
strcmp(ppszTable[i] + sizeof(HASH), pszString) == 0)
return i;
}
return 0;
}
/***************************************************************************
FUNCTION: AddDblToTable
PURPOSE: Add two strings to the table
RETURNS:
COMMENTS:
This function checks to see if the second string has already been
added, and if so, it merely sets the pointer to the original string,
rather then allocating memory for a new copy of the string.
MODIFICATION DATES:
08-Mar-1991 [ralphw]
***************************************************************************/
int STDCALL CTable::AddString(PCSTR pszStr1, PCSTR pszStr2)
{
int ui;
AddString(pszStr1);
if ((ui = IsSecondaryStringInTable(pszStr2)) != 0) {
if (endpos >= maxpos)
IncreaseTableBuffer();
ppszTable[endpos++] = ppszTable[ui];
return endpos - 1;
}
else {
return AddString(pszStr2);
}
}
/***************************************************************************
FUNCTION: IsPrimaryStringInTable
PURPOSE:
RETURNS:
COMMENTS:
MODIFICATION DATES:
03-Apr-1991 [ralphw]
***************************************************************************/
int STDCALL CTable::IsPrimaryStringInTable(PCSTR pszString)
{
int i;
/*
* Skip over as many strings as we can by just checking the first
* letter. This avoids the overhead of the _strcmpi() function call.
* Since the strings aren't necessarily alphabetized, we must trudge
* through the entire table using the _strcmpi() as soon as the first
* character matches.
*/
char chLower = tolower(*pszString);
char chUpper = toupper(*pszString);
for (i = 1; i < endpos; i += 2) {
if (*ppszTable[i] == chLower || *ppszTable[i] == chUpper)
break;
}
for (; i < endpos; i += 2) {
if (_strcmpi(ppszTable[i], pszString) == 0)
return i;
}
return 0;
}
/***************************************************************************
FUNCTION: IsSecondaryStringInTable
PURPOSE:
RETURNS:
COMMENTS:
MODIFICATION DATES:
03-Apr-1991 [ralphw]
***************************************************************************/
int STDCALL CTable::IsSecondaryStringInTable(PCSTR pszString)
{
int i;
/*
* Skip over as many strings as we can by just checking the first
* letter. This avoids the overhead of the _strcmpi() function call.
* Since the strings aren't necessarily alphabetized, we must trudge
* through the entire table using the _strcmpi() as soon as the first
* character matches.
*/
char chLower = tolower(*pszString);
char chUpper = toupper(*pszString);
for (i = 2; i < endpos; i += 2) {
if (*ppszTable[i] == chLower || *ppszTable[i] == chUpper)
break;
}
for (; i < endpos; i += 2) {
if (_strcmpi(ppszTable[i], pszString) == 0)
return i;
}
return 0;
}
/***************************************************************************
FUNCTION: SortTable
PURPOSE: Sort the current buffer
RETURNS:
COMMENTS:
MODIFICATION DATES:
01-Jan-1990 [ralphw]
***************************************************************************/
void CTable::SortTable(void)
{
if (endpos < 3) // don't sort one entry
return;
if (lcid) {
fsSortFlags = fsCompare;
doLcidSort(1, (int) endpos - 1);
}
else
doSort(1, (int) endpos - 1);
}
/***************************************************************************
FUNCTION: doSort
PURPOSE:
RETURNS:
COMMENTS:
Use QSORT algorithm
MODIFICATION DATES:
27-Mar-1990 [ralphw]
***************************************************************************/
void STDCALL CTable::doSort(int left, int right)
{
int last;
if (left >= right) // return if nothing to sort
return;
// REVIEW: should be a flag before trying this -- we may already know
// that they won't be in order.
// Only sort if there are elements out of order.
j = right - 1;
while (j >= left) {
// REVIEW: lstrcmp is NOT case-sensitive!!!
if (strcmp(ppszTable[j],
ppszTable[j + 1]) > 0)
break;
else
j--;
}
if (j < left)
return;
sTmp = (left + right) / 2;
pszTmp = ppszTable[left];
ppszTable[left] = ppszTable[sTmp];
ppszTable[sTmp] = pszTmp;
last = left;
for (j = left + 1; j <= right; j++) {
if (strcmp(ppszTable[j],
ppszTable[left]) < 0) {
sTmp = ++last;
pszTmp = ppszTable[sTmp];
ppszTable[sTmp] = ppszTable[j];
ppszTable[j] = pszTmp;
}
}
pszTmp = ppszTable[left];
ppszTable[left] = ppszTable[last];
ppszTable[last] = pszTmp;
/*
* REVIEW: we need to add some sort of stack depth check to prevent
* overflow of the stack.
*/
if (left < last - 1)
doSort(left, last - 1);
if (last + 1 < right)
doSort(last + 1, right);
}
/***************************************************************************
FUNCTION: CTable::doLcidSort
PURPOSE: Sort using CompareStringA
PARAMETERS:
left
right
RETURNS:
COMMENTS:
MODIFICATION DATES:
03-Jun-1994 [ralphw]
***************************************************************************/
void STDCALL CTable::doLcidSort(int left, int right)
{
int last;
if (left >= right) // return if nothing to sort
return;
// REVIEW: should be a flag before trying this -- we may already know
// that they won't be in order.
// Only sort if there are elements out of order.
j = right - 1;
while (j >= left) {
if (CompareStringA(lcid, fsSortFlags, ppszTable[j], -1,
ppszTable[j + 1], -1) > 2)
break;
else
j--;
}
if (j < left)
return;
sTmp = (left + right) / 2;
pszTmp = ppszTable[left];
ppszTable[left] = ppszTable[sTmp];
ppszTable[sTmp] = pszTmp;
last = left;
for (j = left + 1; j <= right; j++) {
if (CompareStringA(lcid, fsSortFlags, ppszTable[j], -1,
ppszTable[left], -1) < 2) {
sTmp = ++last;
pszTmp = ppszTable[sTmp];
ppszTable[sTmp] = ppszTable[j];
ppszTable[j] = pszTmp;
}
}
pszTmp = ppszTable[left];
ppszTable[left] = ppszTable[last];
ppszTable[last] = pszTmp;
if (left < last - 1)
doLcidSort(left, last - 1);
if (last + 1 < right)
doLcidSort(last + 1, right);
}
/***************************************************************************
FUNCTION: CTable::InitializeTable
PURPOSE: Initializes the table
PARAMETERS:
uInitialSize
RETURNS:
COMMENTS:
Called by constructor and Empty()
MODIFICATION DATES:
23-Feb-1994 [ralphw]
***************************************************************************/
void STDCALL CTable::InitializeTable(void)
{
// Allocate memory for the strings
pszBase = (PSTR) VirtualAlloc(NULL, MAX_STRINGS, MEM_RESERVE,
PAGE_READWRITE);
if (!pszBase) {
OOM();
return;
}
if (!VirtualAlloc(pszBase, cbStrings = TABLE_ALLOC_SIZE, MEM_COMMIT,
PAGE_READWRITE))
OOM();
// Allocate memory for the string pointers
ppszTable = (PSTR *) VirtualAlloc(NULL, MAX_POINTERS, MEM_RESERVE,
PAGE_READWRITE);
if (!ppszTable) {
OOM();
return;
}
if (!VirtualAlloc(ppszTable, cbPointers = TABLE_ALLOC_SIZE, MEM_COMMIT,
PAGE_READWRITE))
OOM();
curpos = 1; // set to one so that sorting works
endpos = 1;
maxpos = cbPointers / sizeof(PSTR);
CurOffset = 0;
lcid = 0;
}
void FASTCALL CTable::SetSorting(LCID lcid, DWORD fsCompareI, DWORD fsCompare)
{
this->lcid = lcid;
this->fsCompareI = fsCompareI;
this->fsCompare = fsCompare;
}
/***************************************************************************
FUNCTION: CTable::AddIndexHitString
PURPOSE: Add an index, a hit number, and a string
PARAMETERS:
index
hit
pszString
RETURNS:
COMMENTS:
MODIFICATION DATES:
27-Oct-1993 [ralphw]
***************************************************************************/
void STDCALL CTable::AddIndexHitString(UINT index, UINT hit, PCSTR pszString)
{
if (endpos >= maxpos)
IncreaseTableBuffer();
if ((ppszTable[endpos] =
TableMalloc(strlen(pszString) + 1 + sizeof(UINT) * 2)) == NULL)
return;
*(UINT*) ppszTable[endpos] = index;
*(UINT*) (ppszTable[endpos] + sizeof(UINT)) = hit;
strcpy(ppszTable[endpos++] + sizeof(UINT) * 2, pszString);
}
/***************************************************************************
FUNCTION: SortTablei
PURPOSE: Case-insensitive sort
RETURNS:
COMMENTS:
MODIFICATION DATES:
01-Jan-1990 [ralphw]
***************************************************************************/
void CTable::SortTablei(void)
{
if (endpos < 3) // don't sort one entry
return;
ASSERT(lcid);
fsSortFlags = fsCompareI | NORM_IGNORECASE;
doLcidSort(1, endpos - 1);
// REVIEW: what is this for?
#if 0
int pos;
for (pos = 1; pos < endpos - 2; pos++) {
if (strlen(ppszTable[pos]) ==
strlen(ppszTable[pos + 1]) &&
CompareStringA(lcid, fsCompare, ppszTable[pos], -1,
ppszTable[pos + 1], -1) == 3) {
PSTR pszTmp = ppszTable[pos];
ppszTable[pos] = ppszTable[pos + 1];
ppszTable[pos + 1] = pszTmp;
if (pos > 2)
pos -= 2;
}
}
#endif
}
UINT STDCALL CTable::GetPosFromPtr(PCSTR psz)
{
int pos = 1;
do {
if (psz == ppszTable[pos])
return pos;
} while (++pos < endpos);
return 0;
}
void STDCALL RemoveTrailingSpaces(PSTR pszString)
{
PSTR psz = pszString + strlen(pszString) - 1;
while (isspace(*psz)) {
if (--psz <= pszString) {
*pszString = '\0';
return;
}
}
psz[1] = '\0';
}
int STDCALL WCmpniSz(LPCSTR psz1, LPCSTR psz2, int cb)
{
if (lcid) {
#ifdef DBCS
int cb1 = min(cb, strlen(psz1));
if (IsDBCSLeadByte(psz1[cb1]))
cb1++;
int cb2 = min(cb, strlen(psz2));
if (IsDBCSLeadByte(psz1[cb2]))
cb2++;
#ifdef _DEBUG
char szBuf1[256];
char szBuf2[256];
strcpy(szBuf1, psz1);
strcpy(szBuf2, psz2);
szBuf1[cb1] = '\0';
szBuf2[cb2] = '\0';
int result = CompareString(lcid, NORM_IGNORECASE, psz1, min(cb1, cb2),
psz2, min(cb1, cb2)) - 2;
#endif
return CompareString(lcid, NORM_IGNORECASE, psz1, min(cb1, cb2),
psz2, min(cb1, cb2)) - 2;
#else
int cb1 = strlen(psz1);
int cb2 = strlen(psz2);
return CompareString(lcid, NORM_IGNORECASE, psz1, min(cb, cb1),
psz2, min(cb, cb2)) - 2;
#endif
}
else
return _strnicmp(psz1, psz2, cb);
}
static void STDCALL ReportAlinkResult(BOOL fSuccess, char chPrefix, PCSTR pszWords)
{
if (hwndParent) {
PSTR pszMsg = (PSTR) lcMalloc(strlen(pszWords) + 100);
wsprintf(pszMsg, "Test%cLink %s: %s\r\n", chPrefix,
GetStringResource(fSuccess ? sidSuccess : sidFail), pszWords);
SendStringToParent(pszMsg);
lcFree(pszMsg);
}
}