windows-nt/Source/XPSP1/NT/enduser/stuff/hhctrl/wwheel.cpp

1784 lines
52 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
// WWheel.cpp - HTML Help Word Wheel support
//
// Covers both KeywordLinks and AssociativeLinks
//
//
// needed for those pesky pre-compiled headers
#include "header.h"
#include "wwheel.h"
#include "animate.h"
#include "strtable.h"
#include "resource.h"
#include "util.h"
// memory leak checks
AUTO_CLASS_COUNT_CHECK(CTitleMapEntry);
AUTO_CLASS_COUNT_CHECK(CTitleMap);
AUTO_CLASS_COUNT_CHECK(CTitleDatabase);
AUTO_CLASS_COUNT_CHECK(CResultsEntry);
AUTO_CLASS_COUNT_CHECK(CResults);
AUTO_CLASS_COUNT_CHECK(CWordWheelEntry);
AUTO_CLASS_COUNT_CHECK(CWordWheel);
AUTO_CLASS_COUNT_CHECK(CWordWheelCompiler);
// taken from "hhsyssrt.h"
// {4662dab0-d393-11d0-9a56-00c04fb68b66}
// HACKHACK: I simply changed the last value of CLSID_ITSysSort from 0xf7 to 0x66
DEFINE_GUID(CLSID_HHSysSort,
0x4662dab0, 0xd393, 0x11d0, 0x9a, 0x56, 0x00, 0xc0, 0x4f, 0xb6, 0x8b, 0x66);
// old format
#define IHHSK666_KEYTYPE_ANSI_SZ ((DWORD) 66630) // NULL-term. MBCS string + extra data
#define IHHSK666_KEYTYPE_UNICODE_SZ ((DWORD) 66631) // NULL-term. Unicode string + extra data
#if 0
// New format
#define IHHSK100_KEYTYPE_ANSI_SZ ((DWORD) 10030) // NULL-term. MBCS string + extra data
#define IHHSK100_KEYTYPE_UNICODE_SZ ((DWORD) 10031) // NULL-term. Unicode string + extra data
#endif
#define IHHSK100_KEYTYPE_ANSI_SZ ((DWORD) 30) // NULL-term. MBCS string + extra data
#define IHHSK100_KEYTYPE_UNICODE_SZ ((DWORD) 31) // NULL-term. Unicode string + extra data
#ifdef _DEBUG
#undef THIS_FILE
static const char THIS_FILE[] = __FILE__;
#endif
// Global Variables
static const CHAR g_szKeywordLinks[] = "KeywordLinks";
static const CHAR g_szAssociativeLinks[] = "AssociativeLinks";
static const CHAR g_szTitleMap[] = "$HHTitleMap";
static const WCHAR g_wszKeywordLinks[] = L"KeywordLinks";
static const WCHAR g_wszAssociativeLinks[] = L"AssociativeLinks";
static const WCHAR g_wszTitleMap[] = L"$HHTitleMap";
static const WCHAR g_wszError[] = L"(ERROR)";
DWORD g_dwError = ((DWORD)-1);
/////////////////////////////////////////////////////////////////////////////
// helpful functions
static const CHAR g_szBusyFile[] = "HTMLHelpKeywordMergingBusy";
static HANDLE g_hFileBusy = NULL;
#define BUSY_FILE_SIZE 32
BOOL IsBusy()
{
BOOL bBusy = FALSE;
HANDLE hFileBusy = NULL;
SetLastError(0);
hFileBusy = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READONLY, 0, BUSY_FILE_SIZE, g_szBusyFile );
if( hFileBusy ) {
if( GetLastError() == ERROR_ALREADY_EXISTS )
bBusy = TRUE;
CloseHandle( hFileBusy );
}
return bBusy;
}
void SetBusy( BOOL bBusy )
{
if( bBusy ) {
if( !g_hFileBusy )
g_hFileBusy = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READONLY, 0, BUSY_FILE_SIZE, g_szBusyFile );
}
else {
if( g_hFileBusy ) {
CloseHandle( g_hFileBusy );
g_hFileBusy = NULL;
}
}
}
int FASTCALL CompareIds( const void* p1, const void* p2 )
{
int iReturn;
CTitleMapEntry* pEntry1= (CTitleMapEntry*) p1;
CTitleMapEntry* pEntry2= (CTitleMapEntry*) p2;
DWORD dwId1 = pEntry1->GetId();
DWORD dwId2 = pEntry2->GetId();
if( dwId1 < dwId2 )
iReturn = -1;
else if ( dwId1 > dwId2 )
iReturn = 1;
else
iReturn = 0;
return iReturn;
}
// check if a specified subfile exists in the specified title
BOOL IsSubFile( PCSTR pszTitlePathname, PCSTR pszSubFile )
{
BOOL bExists = FALSE;
if( pszTitlePathname && pszTitlePathname[0] && pszSubFile && pszSubFile[0] ) {
HRESULT hr = S_OK;
CFileSystem* pFS = new CFileSystem;
if( pFS && SUCCEEDED(hr = pFS->Init()) && SUCCEEDED(hr = pFS->Open( pszTitlePathname )) ) {
CSubFileSystem* pSFS = new CSubFileSystem( pFS );
if( pSFS && SUCCEEDED(pSFS->OpenSub(pszSubFile)) ) {
bExists = TRUE;
delete pSFS;
}
delete pFS;
}
}
return bExists;
}
/////////////////////////////////////////////////////////////////////////////
// class CTitleMap implementation
BOOL CTitleMap::Initialize()
{
// bail out if we are already initialized
if( m_bInit )
return TRUE;
// allocate a MBCS version of our file system pathname
CHAR* psz = NULL;
if( m_pszDatabase && *m_pszDatabase ) {
DWORD dwLen = (DWORD)strlen(m_pszDatabase) + 1;
psz = new char[dwLen];
strcpy(psz,m_pszDatabase);
}
m_pszDatabase = psz;
// set this bool first since we are going to call GetAt()
// which will make a reentrant call if this is not set--
// this would be bad!
m_bInit = TRUE;
// if we have a database, read in the data
if( m_pszDatabase ) {
HRESULT hr = S_OK;
// open the database and read in the subfile
CFileSystem* pDatabase = new CFileSystem;
if( SUCCEEDED(hr = pDatabase->Init()) && SUCCEEDED(hr = pDatabase->Open( GetDatabase() )) ) {
CSubFileSystem* pTitleMap = new CSubFileSystem( pDatabase );
if( SUCCEEDED(hr = pTitleMap->OpenSub( g_szTitleMap ) ) ) {
// format of TitleMap subfile is as follows:
//
// wCount (number of entries)
// wShortNameLen, sShortName, FILETIME, LCID
// ...line above repeated for each entry...
//
ULONG cbRead = 0;
WORD wCount = 0;
pTitleMap->ReadSub( (void*) &wCount, sizeof(wCount), &cbRead );
SetCount( (DWORD) wCount );
for( int iCount = 0; iCount < (int) wCount; iCount++ ) {
WORD wLen = 0;
char szShortName[256];
FILETIME FileTime;
LCID lcid;
pTitleMap->ReadSub( (void*) &wLen, sizeof(wLen), &cbRead );
pTitleMap->ReadSub( (void*) &szShortName, wLen, &cbRead );
szShortName[wLen] = 0;
ASSERT(cbRead != 0) ; // See HH Bug 2807 --- Saved a NULL shortname.
// This means that the CHM file associted with this
// topic probably doesn't exist. Tell the owner of the collection.
pTitleMap->ReadSub( (void*) &FileTime, sizeof(FileTime), &cbRead );
pTitleMap->ReadSub( (void*) &lcid, sizeof(lcid), &cbRead );
GetAt((DWORD)iCount)->SetId( iCount+1 );
GetAt((DWORD)iCount)->SetShortName( szShortName );
GetAt((DWORD)iCount)->SetFileTime( FileTime );
GetAt((DWORD)iCount)->SetLanguage( lcid );
}
}
delete pTitleMap;
}
delete pDatabase;
}
else
m_bInit = FALSE;
return m_bInit;
}
BOOL CTitleMap::Free()
{
if( m_pEntries ) {
delete [] m_pEntries;
m_pEntries = NULL;
m_dwCount = HHWW_ERROR;
}
if( m_pszDatabase ) {
delete [] (CHAR*) m_pszDatabase;
m_pszDatabase = NULL;
}
m_bInit = FALSE;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// class CTitleDatabase implementation (Shared Centaur object)
void CTitleDatabase::_CTitleDatabase()
{
m_bInit = FALSE;
m_pDatabase = NULL;
m_pwszDatabase = NULL;
m_pszDatabase = NULL;
m_bCollection = FALSE;
m_pTitleMap = NULL;
m_pKeywordLinks = NULL;
m_pAssociativeLinks = NULL;
m_pCollection = NULL;
m_pTitle = NULL;
#ifdef CHIINDEX
m_bAnimation = TRUE; // display animation
#endif
}
CTitleDatabase::CTitleDatabase( CExCollection* pCollection )
{
_CTitleDatabase();
m_pCollection = pCollection;
}
CTitleDatabase::CTitleDatabase( CExTitle* pTitle )
{
_CTitleDatabase();
m_pTitle = pTitle;
}
CTitleDatabase::CTitleDatabase( const WCHAR* pwszDatabase )
{
_CTitleDatabase();
m_pwszDatabase = pwszDatabase;
}
CTitleDatabase::CTitleDatabase( const CHAR* pszDatabase )
{
_CTitleDatabase();
m_pszDatabase = pszDatabase;
}
CTitleDatabase::~CTitleDatabase()
{
Free();
}
BOOL CTitleDatabase::Initialize(CHAR *pszFileName)
{
BOOL bReturn = FALSE;
HRESULT hr = S_OK;
// bail out if we are already initialized
if( m_bInit )
return TRUE;
// bail out if collection or title not specified
if( !(m_pCollection || m_pTitle || m_pwszDatabase || m_pszDatabase ) )
return FALSE;
// set the hourglass cursor
CHourGlass HourGlass;
// get the database name
if( m_pCollection ) {
m_pTitle = m_pCollection->GetFirstTitle();
m_bCollection = ( m_pCollection && (m_pCollection->GetRefedTitleCount() > 1) );
if( m_bCollection )
m_pszDatabase = m_pCollection->GetLocalStoragePathname(".chw");
else
m_pszDatabase = m_pTitle->GetIndexFileName();
}
else if( m_pTitle ) {
m_pszDatabase = m_pTitle->GetIndexFileName();
}
if (pszFileName)
{
char drive[_MAX_PATH], dir[_MAX_PATH];
splitpath(m_pszDatabase, drive, dir, NULL, NULL);
if (drive[0] && drive[strlen(drive)-1] != '\\' && dir[0] != '\\')
strcat(drive, "\\");
strcat(drive, dir);
if (drive[strlen(drive)-1] != '\\' && pszFileName[0] != '\\')
strcat(drive, "\\");
strcat(drive, pszFileName);
strcpy(m_szFullPath, drive);
m_pszDatabase = m_szFullPath;
}
// allocate UNICODE and MBCS versions of our file system pathname
WCHAR* pwsz = NULL;
CHAR* psz = NULL;
if( m_pszDatabase && *m_pszDatabase ) {
DWORD dwLen = (DWORD)strlen(m_pszDatabase) + 1;
pwsz = new WCHAR[dwLen];
MultiByteToWideChar(CP_ACP, 0, m_pszDatabase, -1, pwsz, dwLen);
psz = new char[dwLen];
strcpy(psz,m_pszDatabase);
}
else if( m_pwszDatabase && *m_pwszDatabase ) {
DWORD dwLen = wcslen(m_pwszDatabase) + 1;
pwsz = new WCHAR[dwLen];
wcscpy(pwsz,m_pwszDatabase);
psz = new char[dwLen];
WideCharToMultiByte(CP_ACP, 0, m_pwszDatabase, -1, psz, dwLen, NULL, NULL);
}
m_pwszDatabase = pwsz;
m_pszDatabase = psz;
// bail out if file system pathname not specified
if( !m_pwszDatabase || !*m_pwszDatabase )
return FALSE;
// do a merge check
if( m_bCollection ) {
if( !MergeWordWheels() ) {
m_bCollection = FALSE;
m_pCollection = NULL;
Free();
return Initialize();
}
}
// get ITDatabase ptr.
if( SUCCEEDED(hr = CoCreateInstance(CLSID_IITDatabaseLocal, NULL, CLSCTX_INPROC_SERVER,
IID_IITDatabase, (void**)&m_pDatabase) ) ) {
// if the file exists then this is good enough to say we initialized it
if( IsFile( m_pszDatabase ) ) {
bReturn = TRUE;
// Open the database
if( SUCCEEDED(hr = m_pDatabase->Open(NULL, m_pwszDatabase, 0) ) ) {
bReturn = TRUE;
}
}
}
// create our word wheels
CTitleInformation* pInfo = NULL;
if( (m_pTitle && (pInfo = m_pTitle->GetInfo())) || !m_pTitle ) {
if( (m_pTitle && pInfo->IsKeywordLinks()) ||
(!m_pTitle && IsSubFile( m_pszDatabase, "$WWKeywordLinks\\Data" ) ) ) {
m_pKeywordLinks = new CWordWheel( this, g_szKeywordLinks );
}
if( (m_pTitle && pInfo->IsAssociativeLinks()) ||
(!m_pTitle && IsSubFile( m_pszDatabase, "$WWAssociativeLinks\\Data" ) ) ) {
m_pAssociativeLinks = new CWordWheel( this, g_szAssociativeLinks );
}
}
if( !bReturn ) {
Free();
m_bInit = FALSE;
}
else
m_bInit = TRUE;
return bReturn;
}
BOOL CTitleDatabase::Free()
{
BOOL bReturn = FALSE;
if( m_pTitleMap ) {
delete m_pTitleMap;
m_pTitleMap = NULL;
}
if( m_pKeywordLinks ) {
delete m_pKeywordLinks;
m_pKeywordLinks = NULL;
}
if( m_pAssociativeLinks ) {
delete m_pAssociativeLinks;
m_pAssociativeLinks = NULL;
}
if( m_pDatabase ) {
m_pDatabase->Close();
m_pDatabase->Release();
m_pDatabase = NULL;
}
if( m_pwszDatabase ) {
delete [] (WCHAR*) m_pwszDatabase;
m_pwszDatabase = NULL;
}
if( m_pszDatabase ) {
delete [] (CHAR*) m_pszDatabase;
m_pszDatabase = NULL;
}
m_bInit = FALSE;
bReturn = TRUE;
return bReturn;
}
BOOL CTitleDatabase::MergeWordWheels()
{
BOOL bReturn = FALSE;
#ifndef CHIINDEX
// get the application window
int iTry = 0;
HWND hWndApp = GetActiveWindow();
HWND hWndDesktop = GetDesktopWindow();
HWND hWnd = GetParent( hWndApp );
if( hWnd ) {
while( hWnd != hWndDesktop ) {
if( iTry++ == 16 ) {
hWndApp = GetActiveWindow();
if( !IsValidWindow( hWndApp ) )
hWndApp = NULL;
break;
}
hWndApp = hWnd;
hWnd = GetParent( hWndApp );
}
}
#endif
// start the animation
#ifndef CHIINDEX
if( !IsBusy() )
StartAnimation( IDS_CREATING_INDEX, hWndApp );
#endif
// if another merge is in progress then wait
// if we are currently busy then pretend to generate the file
#ifndef CHIINDEX
while( IsBusy() ) {
Sleep( 100 );
NextAnimation();
}
#endif
// set the busy state
SetBusy( TRUE );
// check if we need to merge or not
BOOL bMerge = CheckWordWheels();
// do the merge if necessary
if( bMerge ) {
#if 0 // MsgBox causes a reentrant problem--so don't do this!
if( !m_pTitle->GetInfo()->IsNeverPromptOnMerge() ) {
if( MsgBox( IDS_MERGE_PROMPT, MB_OKCANCEL ) != IDOK )
bMerge = FALSE;
}
#endif
if( bMerge ) {
bReturn = BuildWordWheels();
}
}
else
bReturn = TRUE;
#ifndef CHIINDEX
// stop the animation
StopAnimation();
#endif
// reset the busy state
SetBusy( FALSE );
return bReturn;
}
BOOL CTitleDatabase::CheckWordWheels()
{
BOOL bReturn = FALSE;
//
// We perform a merge under the following conditions:
//
// 1. merged file does not exist.
// 2. title count has changed
// 3. any title has changed (updated, removed, added)
BOOL bMerge = FALSE;
DWORD dwCount = 0;
CExTitle* pTitle = NULL;
// We have to always create the title map even if we do not need to merge
// first check if each title can be initialized
// so we know how many real titles we have
// (the collection may contain bogus entries so we need to exclude these)
DWORD dwCount0 = m_pCollection->GetRefedTitleCount();
// create our title map of the existing files
dwCount = dwCount0;
m_pTitleMap = new CTitleMap;
m_pTitleMap->SetCount( dwCount );
pTitle = m_pCollection->GetFirstTitle();
for( int iTitle = 0; iTitle < (int) dwCount; iTitle++ ) {
CTitleMapEntry* pEntry = m_pTitleMap->GetAt( iTitle );
pEntry->SetTitle( pTitle );
if( pTitle )
pTitle = pTitle->GetNext();
}
// 1. merged file does not exist.
bMerge = !IsFile( m_pszDatabase );
if( !bMerge ) {
// if we cannot read the file since read access is denied then wait
// for the file to be readable again
while( TRUE ) {
HANDLE hFile = CreateFile( m_pszDatabase, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile != INVALID_HANDLE_VALUE ) {
CloseHandle( hFile );
break;
}
Sleep( 100 );
NextAnimation();
}
// 2. title count has changed
CTitleMap* pTitleMapSaved = new CTitleMap( m_pszDatabase );
DWORD dwCountSaved = pTitleMapSaved->GetCount();
bMerge = !( dwCountSaved == dwCount );
// 3. any title has changed (updated, removed, added)
if( !bMerge ) {
// walk the title list and compare it's title entries
// to the one we just read in from the title map.
// If they differ, then rebuild the merged file
// otherwise, update the mapping Ids for the title map.
for( int iTitleSaved = 0; iTitleSaved < (int) dwCountSaved; iTitleSaved++ ) {
BOOL bMatch = FALSE;
CTitleMapEntry* pEntrySaved = pTitleMapSaved->GetAt( iTitleSaved );
const CHAR* pszShortNameSaved = pEntrySaved->GetShortName();
FILETIME FileTimeSaved = pEntrySaved->GetFileTime();
LCID lcidSaved = pEntrySaved->GetLanguage();
DWORD dwIdSaved = pEntrySaved->GetId();
for( int iTitle = 0; iTitle < (int) dwCount; iTitle++ ) {
CTitleMapEntry* pEntry = m_pTitleMap->GetAt( iTitle );
const CHAR* pszShortName = pEntry->GetShortName();
FILETIME FileTime = pEntry->GetFileTime();
LCID lcid = pEntry->GetLanguage();
// BUG 2807: the CHW file on the system had NULL for the "=pdobj" title.
// This caused an access violation in SetShortName. [dalero]
// A NULL short name will be saved to the CHW file, if we cannot find the CHM.
if( pszShortNameSaved && !lstrcmpi( pszShortNameSaved, pszShortName ) ) {
if( !CompareFileTime( &FileTimeSaved, &FileTime ) ) {
if( lcidSaved == lcid ) {
// Note, some dummy (MSDN) may have more than one copy of the identical
// title but saved under a different filename.
//
// Thus, we need to make sure that each Id gets assigned to a title
// and we do this by making sure the Id is not set already.
// If it is set, the continue looking for the next free spot that
// contains the same title information.
if( !pEntry->GetId() ) { // empty Id
pEntry->SetId( dwIdSaved ); // set the Id
pEntry->SetShortName( pszShortNameSaved ); // set the Short Name
bMatch = TRUE;
break;
}
}
}
}
}
if( (bMerge = !bMatch) == TRUE )
break;
}
}
delete pTitleMapSaved;
}
if( !bMerge ) {
// sort the map entries in Id order
m_pTitleMap->Sort( CompareIds );
}
bReturn = bMerge;
return bReturn;
}
BOOL CTitleDatabase::BuildWordWheels()
{
BOOL bReturn = FALSE;
// delete existing file
if( IsFile( m_pszDatabase ) )
DeleteFile( m_pszDatabase );
DWORD dwCount = m_pTitleMap->GetCount();
CExTitle* pTitle = NULL;
// now merge the keywords (using the title map)
CWordWheelCompiler* pCompiler = new CWordWheelCompiler( m_pszDatabase,
g_wszKeywordLinks, g_wszAssociativeLinks );
if( pCompiler->Initialize() != S_OK )
return FALSE;
IITBuildCollect* pBuildCollect = NULL;
IITPropList* pPropList = pCompiler->m_pPropList;
// #define HH_SLOW_MERGE // if you want merging to be slow
#ifndef HH_SLOW_MERGE
// create the property items upfront, and just update them as they change
pPropList->Set(STDPROP_UID, (DWORD)0, PROP_ADD );
pPropList->Set(STDPROP_SORTKEY, (VOID*) NULL, 0, PROP_ADD );
#endif
CWordWheel* pWordWheel = NULL;
// set our title map Ids (in order), create our databases,
// and create our word wheels
for( int iTitleWalk = 0; iTitleWalk < (int) dwCount; iTitleWalk++ )
{
#ifdef CHIINDEX
if ( m_bAnimation )
{
#endif
NextAnimation();
Sleep(0);
#ifdef CHIINDEX
}
#endif
CTitleMapEntry* pEntry = m_pTitleMap->GetAt( iTitleWalk );
pEntry->SetId( iTitleWalk+1 );
CTitleDatabase* pDatabase = NULL;
CWordWheel* pKeywordLinks = NULL;
CWordWheel* pAssociativeLinks = NULL;
pDatabase = new CTitleDatabase( pEntry->GetTitle()->GetIndexFileName() );
if( !pDatabase->Initialize() ) {
delete pDatabase;
pDatabase = NULL;
// Can't do anything else without a CTitleDatabase.
continue;
}
pEntry->SetDatabase( pDatabase );
pEntry->SetKeywordLinks( pDatabase->GetKeywordLinks() );
pEntry->SetAssociativeLinks( pDatabase->GetAssociativeLinks() );
// loop through each word wheel type
for( int iWordWheel=1; iWordWheel<=2; iWordWheel++ ) {
if( iWordWheel == 1 ) {
pWordWheel = pDatabase->GetKeywordLinks();
pBuildCollect = pCompiler->m_pBuildCollectKeywordLinks;
}
if( iWordWheel == 2 ) {
pWordWheel = pDatabase->GetAssociativeLinks();
pBuildCollect = pCompiler->m_pBuildCollectAssociativeLinks;
}
if( !pWordWheel )
continue;
// add each keyword
DWORD dwCount = pWordWheel->GetCount();
for( int iKeyword = 0; iKeyword < (int) dwCount; iKeyword++ ) {
// update the animation
if( !(iKeyword%200) ) {
#ifdef CHIINDEX
if (m_bAnimation) {
#endif
NextAnimation();
Sleep(0);
#ifdef CHIINDEX
}
#endif
}
DWORD dwKeyword = iKeyword;
BYTE KeywordObject[HHWW_MAX_KEYWORD_OBJECT_SIZE];
if( SUCCEEDED( pWordWheel->GetWordWheel()->Lookup( dwKeyword, KeywordObject, HHWW_MAX_KEYWORD_OBJECT_SIZE ) ) ) {
int iLen = wcslen( (WCHAR*) KeywordObject );
int iOffset = sizeof(WCHAR) * (iLen+1);
HHKEYINFO* pInfo = (HHKEYINFO*)(((DWORD_PTR)(&KeywordObject)+iOffset));
iOffset += sizeof(HHKEYINFO);
if( pInfo->wFlags & HHWW_SEEALSO )
iOffset += sizeof(WCHAR) * (wcslen( (WCHAR*) (((DWORD_PTR)&KeywordObject)+iOffset) ) + 1);
else { // change the UIDs to 12:20 format
DWORD dwCount = pInfo->dwCount;
UNALIGNED DWORD* pdwURLId = (DWORD*)(((DWORD_PTR)&KeywordObject)+iOffset);
for( int iURLId = 0; iURLId < (int) dwCount; iURLId++ ) {
DWORD dwURLId = *(pdwURLId+iURLId);
dwURLId = ((iTitleWalk+1)<<20) + dwURLId; // 12:20 format
*((UNALIGNED DWORD*)(((DWORD_PTR)&KeywordObject)+iOffset)) = dwURLId;
iOffset += sizeof(DWORD);
}
}
// add the new entry to the new word wheel
// Note, we must set SZ_WWDEST_OCC and not SZ_WWDEST_KEY
// otherwise ITCC crashes
#ifdef HH_SLOW_MERGE
pPropList->Set(STDPROP_UID, (DWORD)0, PROP_ADD );
pPropList->Set(STDPROP_SORTKEY, (VOID*) KeywordObject, iOffset, PROP_ADD );
pBuildCollect->SetEntry(SZ_WWDEST_OCC, pPropList);
pPropList->Clear();
#else
// simply update the property items -- no need to add/clear them each time
pPropList->Set(STDPROP_SORTKEY, (VOID*) KeywordObject, iOffset, PROP_UPDATE );
pBuildCollect->SetEntry(SZ_WWDEST_OCC, pPropList);
#endif
}
}
}
if( pDatabase ) {
delete pDatabase;
pEntry->SetDatabase( NULL );
}
}
#ifndef HH_SLOW_MERGE
pPropList->Clear();
#endif
// build it
#ifdef CHIINDEX
if ( m_bAnimation )
{
#endif
NextAnimation();
Sleep(0);
#ifdef CHIINDEX
}
#endif
pCompiler->Build();
// delete the compiler
delete pCompiler;
// open the database file
CFileSystem* pDatabaseMap = new CFileSystem();
if( FAILED( pDatabaseMap->Init() ) || FAILED( pDatabaseMap->Open( m_pszDatabase, STGM_READWRITE | STGM_SHARE_EXCLUSIVE ) ) ) {
// stop the animation
StopAnimation();
SetBusy( FALSE );
return FALSE;
}
// sort the map entries in Id order
m_pTitleMap->Sort( CompareIds );
// write out the new title map
CSubFileSystem* pTitleMap = new CSubFileSystem( pDatabaseMap );
pTitleMap->CreateSub( g_szTitleMap );
// walk the collection and write out the map
WORD wValue = (WORD) dwCount;
pTitleMap->WriteSub( (const void*) &wValue, sizeof(wValue) );
for( int iTitle = 0; iTitle < (int) dwCount; iTitle++ ) {
CTitleMapEntry* pEntry = m_pTitleMap->GetAt( iTitle );
const CHAR* pszShortName = pEntry->GetShortName(); // NOTE: pszShortName may be NULL. If the CHM file does not exists. It can be NULL!!!
FILETIME FileTime = pEntry->GetFileTime();
LCID lcid = pEntry->GetLanguage();
ASSERT(pszShortName != NULL) ; // NOTE: If you get this assert it most likely means that the CHM file associated with this topic doesn't exist.
DWORD dwLen = 0 ;
if (pszShortName)
{
dwLen = (DWORD)strlen(pszShortName);
}
wValue = (WORD) dwLen;
pTitleMap->WriteSub( (const void*) &wValue, sizeof(wValue) );
pTitleMap->WriteSub( pszShortName, (int) dwLen );
pTitleMap->WriteSub( (const void*) &FileTime, sizeof(FileTime) );
pTitleMap->WriteSub( (const void*) &lcid, sizeof(lcid) );
}
delete pTitleMap;
delete pDatabaseMap;
bReturn = TRUE;
return bReturn;
}
/////////////////////////////////////////////////////////////////////////////
// class CWordWheel implementation
void CWordWheel::_CWordWheel()
{
m_bInit = FALSE;
m_pWordWheel = NULL;
m_dwCount = HHWW_ERROR;
m_pszWordWheelIn = NULL;
m_pwszWordWheelIn = NULL;
m_pwszWordWheel = NULL;
m_dwRefCount = 0;
}
CWordWheel::CWordWheel( CTitleDatabase* pDatabase, const WCHAR* pwszWordWheel, DWORD dwTitleId )
{
_CWordWheel();
m_pDatabase = pDatabase;
m_pwszWordWheelIn = pwszWordWheel;
m_dwTitleId = dwTitleId;
}
CWordWheel::CWordWheel( CTitleDatabase* pDatabase, const CHAR* pszWordWheel, DWORD dwTitleId )
{
_CWordWheel();
m_pDatabase = pDatabase;
m_pszWordWheelIn = pszWordWheel;
m_dwTitleId = dwTitleId;
}
CWordWheel::~CWordWheel()
{
Free();
}
DWORD CWordWheel::AddRef()
{
return ++m_dwRefCount;
}
DWORD CWordWheel::Release()
{
if( m_dwRefCount )
--m_dwRefCount;
return m_dwRefCount;
}
BOOL CWordWheel::Initialize()
{
// bail out if we are already initialized
if( m_bInit )
return TRUE;
// allocate a UNICODE version of our word wheel name
if( m_pszWordWheelIn && *m_pszWordWheelIn ) {
DWORD dwLen = (DWORD)strlen(m_pszWordWheelIn) + 1;
m_pwszWordWheel = new WCHAR[dwLen+1];
MultiByteToWideChar(CP_ACP, 0, m_pszWordWheelIn, -1, (WCHAR*) m_pwszWordWheel, dwLen);
}
else if( m_pwszWordWheelIn && *m_pwszWordWheelIn ) {
DWORD dwLen = wcslen(m_pwszWordWheelIn) + 1;
m_pwszWordWheel = new WCHAR[dwLen];
wcscpy( (WCHAR*) m_pwszWordWheel, m_pwszWordWheelIn );
}
// bail out if word wheel name not specified
if( !m_pwszWordWheel || !*m_pwszWordWheel )
return FALSE;
BOOL bReturn = FALSE;
HRESULT hr = S_OK;
// get ITWordWheel ptr.
if( SUCCEEDED(hr = CoCreateInstance(CLSID_IITWordWheelLocal, NULL, CLSCTX_INPROC_SERVER,
IID_IITWordWheel, (void**)&m_pWordWheel) ) ) {
// open the word wheel
if( SUCCEEDED(hr = m_pWordWheel->Open( m_pDatabase->GetDatabase(), m_pwszWordWheel, ITWW_OPEN_NOCONNECT) ) ) {
bReturn = TRUE;
}
}
if( !bReturn ) {
Free();
m_bInit = FALSE;
}
else
m_bInit = TRUE;
return bReturn;
}
BOOL CWordWheel::Free()
{
BOOL bReturn = FALSE;
if( m_pWordWheel ) {
m_pWordWheel->Close();
m_pWordWheel->Release();
m_pWordWheel = NULL;
}
if( m_pwszWordWheel ) {
delete [] (WCHAR*) m_pwszWordWheel;
m_pwszWordWheel = NULL;
}
if( m_pwszWordWheelIn )
m_pwszWordWheelIn = NULL;
if( m_pszWordWheelIn )
m_pszWordWheelIn = NULL;
m_bInit = FALSE;
bReturn = TRUE;
return bReturn;
}
DWORD CWordWheel::GetCount()
{
DWORD dwReturn = HHWW_ERROR;
if( Init() ) {
if( m_dwCount == HHWW_ERROR ) {
LONG nCount;
if( SUCCEEDED(m_pWordWheel->Count(&nCount)) )
dwReturn = m_dwCount = nCount;
}
else
dwReturn = m_dwCount;
}
return dwReturn;
}
/////////////////////////////////////////////////////////////////////////////
// CWordWheel::GetIndex
//
// params:
//
// pwszKeywordIn - keyword to lookup
//
// bFragment - set this to TRUE if only looking for a partial match
// such as when the user types in a string fragment
// in the Index tab
//
// pdwIndexLast - If not NULL, then the function should return the
// first equivalent index and set the contents of this
// argument to the index of the last equivalent keyword.
// And equivalent keyword is where the root word (less
// and special prefixes) is the same regardless of case.
// For example, "_open", "open", "Open", and "OPEN" are
// equivalent keywords. This should be used for F1 lookups
// and A/KLink lookups as well.
//
// Note: if bFragment is TRUE and pdwIndexLast is non-NULL then
// bFragment will be set to FALSE. If neither, then we look for an
// exact match only.
//
DWORD CWordWheel::GetIndex( const WCHAR* pwszKeywordIn, BOOL bFragment, DWORD* pdwIndexLast )
{
DWORD dwIndexFirst = HHWW_ERROR;
// we cannot do both a fragment lookup and an equivalent lookup
if( pdwIndexLast )
bFragment = FALSE;
if( Init() && pwszKeywordIn && *pwszKeywordIn ) {
if( GetCount() == (DWORD) -1 )
return dwIndexFirst;
// since we are using a pluggable sort object the input to Lookup must
// be in the save format as the sort object itself. That is, we need
// to add an HHKEYINFO structure to the end of this string and fill in
// the data properly since the CHHSysSort::GetSize function in our
// pluggable sort module will get this object and expect in in that format
//
// Note, if it did not contain the trailing struct it can and will fault.
BYTE KeywordObject[HHWW_MAX_KEYWORD_OBJECT_SIZE];
WCHAR* pwszKeywordObject = (WCHAR*) KeywordObject;
wcsncpy( pwszKeywordObject, pwszKeywordIn, HHWW_MAX_KEYWORD_LENGTH );
pwszKeywordObject[HHWW_MAX_KEYWORD_LENGTH] = 0;
#ifdef _DEBUG
int iMaxObject = HHWW_MAX_KEYWORD_OBJECT_SIZE;
int iMaxLen = HHWW_MAX_KEYWORD_LENGTH;
int iLen = wcslen(pwszKeywordObject);
#endif
HHKEYINFO Info;
Info.wFlags = 0;
Info.wLevel = 0;
Info.dwLevelOffset = 0;
Info.dwFont = 0;
Info.dwCount = 0;
DWORD dwLength = sizeof(WCHAR) * (wcslen(pwszKeywordObject) + 1);
*((HHKEYINFO*)(((DWORD_PTR)pwszKeywordObject)+dwLength)) = Info;
// There are three kinds of lookup matches:
//
// 1. Exact - found hits must be completely indentical to the
// keyword we are looking up.
// "FOOBAR" == "FOOBAR"
//
// 2. Equivalent - the keyword must only differ by case or prefixes
// and we continue to find all keywords, not just the
// first one, that meets this criteria.
// "_foobar" == "FooBar" == "FOOBAR"
//
// 3. Fragment - find the first hit where only the first non-prefixed
// characters need to match while ignoring case.
// "~fo" == "FOOBAR" (assuming nothing else was a closer match)
// We have three ways that we perform such lookups:
//
// 1. Index tab - Tries Exact first then uses Fragment lookups.
//
// 2. F1 Lookups - Tries Exact first then uses Equivalent lookups.
//
// 3. A/Klinks - Tries Exact first then uses Equivalent lookups.
// Centaur just doesn't do the right thing for non-exact,
// a.k.a. prefix, lookups!
//
// For exact matches, try the first found keyword and verify it.
// If it does not match, then try a fragment lookup.
//
// For fragment matches, we need to first try the exact match
// technique noted above.
// If it does not match, then try a partial (size of lookup word),
// case-insensitive lookup.
// If it does not match, and then try the *next* entry in the same fashion.
BYTE KeywordObjectTry[HHWW_MAX_KEYWORD_OBJECT_SIZE];
WCHAR* pwszKeywordObjectTry = (WCHAR*) KeywordObjectTry;
// Try exact matches first
if( SUCCEEDED(m_pWordWheel->Lookup( &KeywordObject, (BOOL) TRUE, (LONG*) &dwIndexFirst )) ) {
if( SUCCEEDED(m_pWordWheel->Lookup( dwIndexFirst, &KeywordObjectTry, sizeof(KeywordObjectTry) )) ) {
if( !(wcscmp( pwszKeywordObjectTry, pwszKeywordObject ) == 0) ) {
dwIndexFirst = HHWW_ERROR;
}
}
}
// Try equivalent match next
if( pdwIndexLast && (dwIndexFirst == HHWW_ERROR) ) {
// skip over any special chars
const WCHAR* pwszKeyword = NULL;
for( pwszKeyword = pwszKeywordObject; pwszKeyword; pwszKeyword++ ) {
if( !(( (*pwszKeyword) == L'_') || ( (*pwszKeyword) == L'~')) )
break;
}
// Try a prefix lookup
if( SUCCEEDED(m_pWordWheel->Lookup( pwszKeyword, (BOOL) FALSE, (LONG*) &dwIndexFirst )) ) {
if( SUCCEEDED(m_pWordWheel->Lookup( dwIndexFirst, &KeywordObjectTry, sizeof(KeywordObjectTry) )) ) {
// validate the hit, if it fails, try the next index value
DWORD dwTryFirst = dwIndexFirst;
dwIndexFirst = HHWW_ERROR;
for( DWORD dwTry = dwTryFirst; dwTry <= dwTryFirst+1 ; dwTry++ ) {
if( SUCCEEDED(m_pWordWheel->Lookup( dwTry, &KeywordObjectTry, sizeof(KeywordObjectTry) )) ) {
// try it without prefixes in the input string
if( wcsicmp( pwszKeywordObjectTry, pwszKeyword ) == 0 ) {
dwIndexFirst = dwTry;
break;
} // try it with the prefixes back in
else if( ((DWORD_PTR) pwszKeyword) != ((DWORD_PTR) pwszKeywordObject) ) {
if( wcsicmp( pwszKeywordObjectTry, pwszKeywordObject ) == 0 ) {
dwIndexFirst = dwTry;
break;
}
} // ignore prefixes in found hit
else if( (*pwszKeywordObjectTry == L'_') || (*pwszKeywordObjectTry == L'~') ) {
const WCHAR* pwszKeyword = NULL;
for( pwszKeyword = pwszKeywordObjectTry; pwszKeyword; pwszKeyword++ ) {
if( !(( (*pwszKeyword) == L'_') || ( (*pwszKeyword) == L'~')) )
break;
}
if( wcsicmp( pwszKeywordObject, pwszKeyword ) == 0 ) {
dwIndexFirst = dwTry;
break;
}
}
}
}
}
}
}
// Try fragment match last
if( bFragment && (dwIndexFirst == HHWW_ERROR) ) {
// skip over any special chars
const WCHAR* pwszKeyword = NULL;
for( pwszKeyword = pwszKeywordObject; pwszKeyword; pwszKeyword++ ) {
if( !(( (*pwszKeyword) == L'_') || ( (*pwszKeyword) == L'~')) )
break;
}
while( TRUE ) {
if( SUCCEEDED(m_pWordWheel->Lookup( pwszKeyword, (BOOL) FALSE, (LONG*) &dwIndexFirst )) ) {
int iLen = wcslen( pwszKeyword );
if( SUCCEEDED(m_pWordWheel->Lookup( dwIndexFirst, &KeywordObjectTry, sizeof(KeywordObjectTry) )) ) {
// validate the hit, if it fails, try the next index value
DWORD dwTryFirst = dwIndexFirst;
dwIndexFirst = HHWW_ERROR;
for( DWORD dwTry = dwTryFirst; dwTry <= dwTryFirst+1; dwTry++ ) {
if( SUCCEEDED(m_pWordWheel->Lookup( dwTry, &KeywordObjectTry, sizeof(KeywordObjectTry) )) ) {
// try it without prefixes in the input string
int iLen2 = min(iLen, (int) wcslen(pwszKeywordObjectTry));
if( wcsnicmp( pwszKeywordObjectTry, pwszKeyword, iLen2 ) == 0 ) {
dwIndexFirst = dwTry;
break;
} // try it with the prefixes back in
else if( ((DWORD_PTR) pwszKeyword) != ((DWORD_PTR) pwszKeywordObject) ) {
int iLen = wcslen( pwszKeywordObject );
if( wcsnicmp( pwszKeywordObjectTry, pwszKeywordObject, iLen2 ) == 0 ) {
dwIndexFirst = dwTry;
break;
}
} // ignore prefixes in found hit
else if( (*pwszKeywordObjectTry == L'_') || (*pwszKeywordObjectTry == L'~') ) {
const WCHAR* pwszKeyword = NULL;
for( pwszKeyword = pwszKeywordObjectTry; pwszKeyword; pwszKeyword++ ) {
if( !(( (*pwszKeyword) == L'_') || ( (*pwszKeyword) == L'~')) )
break;
}
if( wcsnicmp( pwszKeywordObject, pwszKeyword, iLen2 ) == 0 ) {
dwIndexFirst = dwTry;
break;
}
} // for framgent lookups only, when all else fails,
// simply trust the first try (works half the time)
else if( dwTry == dwTryFirst+1 ) {
dwIndexFirst = dwTryFirst;
break;
}
}
}
}
}
// for fragments, we need to keep trying until we get a hit by
// trimming the trailing chars one at a time until we get a match
if( KeywordObject[0] && (dwIndexFirst == HHWW_ERROR) ) {
DWORD dwLength = wcslen(pwszKeywordObject);
if( dwLength <= 1 )
break;
pwszKeywordObject[dwLength-1] = L'\0';
*((HHKEYINFO*)(((DWORD_PTR)KeywordObject)+(dwLength*sizeof(WCHAR)))) = Info;
continue;
}
break;
}
}
// if equivalent match found then find the real first and last equivalent
if( pdwIndexLast && (dwIndexFirst != HHWW_ERROR) ) {
DWORD dwIndexFirstTry = dwIndexFirst;
DWORD dwIndexLastTry = dwIndexFirst;
// skip over any special chars
const WCHAR* pwszKeyword = NULL;
for( pwszKeyword = pwszKeywordObject; pwszKeyword; pwszKeyword++ ) {
if( !(( (*pwszKeyword) == L'_') || ( (*pwszKeyword) == L'~')) )
break;
}
// find the first one
for( dwIndexFirstTry--; dwIndexFirstTry != (DWORD)-1; dwIndexFirstTry-- ) {
if( SUCCEEDED(m_pWordWheel->Lookup( dwIndexFirstTry, &KeywordObjectTry, sizeof(KeywordObjectTry) )) ) {
const WCHAR* pwszBuffer = NULL;
for( pwszBuffer = pwszKeywordObjectTry; pwszBuffer; pwszBuffer++ ) {
if( !(( (*pwszBuffer) == L'_') || ( (*pwszBuffer) == L'~')) )
break;
}
if( !(_wcsicmp( pwszBuffer, pwszKeyword ) == 0) )
break;
dwIndexFirst = dwIndexFirstTry;
}
}
// find the last one
*pdwIndexLast = dwIndexLastTry;
for( dwIndexLastTry++; dwIndexLastTry <= m_dwCount; dwIndexLastTry++ ) {
if( SUCCEEDED(m_pWordWheel->Lookup( dwIndexLastTry, &KeywordObjectTry, sizeof(KeywordObjectTry) )) ) {
const WCHAR* pwszBuffer = NULL;
for( pwszBuffer = pwszKeywordObjectTry; pwszBuffer; pwszBuffer++ ) {
if( !(( (*pwszBuffer) == L'_') || ( (*pwszBuffer) == L'~')) )
break;
}
if( !(_wcsicmp( pwszBuffer, pwszKeyword ) == 0) )
break;
*pdwIndexLast = dwIndexLastTry;
}
}
}
}
return dwIndexFirst;
}
DWORD CWordWheel::GetIndex( const CHAR* pszKeyword, BOOL bFragment, DWORD* pdwIndexLast )
{
DWORD dwReturn = HHWW_ERROR;
if( pszKeyword && *pszKeyword ) {
WCHAR wszKeyword[HHWW_MAX_KEYWORD_LENGTH+1];
if( MultiByteToWideChar(CP_ACP, 0, pszKeyword, -1, wszKeyword, HHWW_MAX_KEYWORD_LENGTH+1) )
dwReturn = GetIndex( wszKeyword, bFragment, pdwIndexLast );
}
return dwReturn;
}
BOOL CWordWheel::GetString( DWORD dwKeyword, WCHAR* pwszBuffer, DWORD cchBuffer, BOOL bFull, BOOL bCacheAll )
{
BOOL bReturn = FALSE;
if( pwszBuffer && cchBuffer>0 ) {
if( (bReturn = GetIndexData( dwKeyword, bCacheAll ) ) ) {
DWORD dwLength = 0;
if( bFull )
dwLength = wcslen(m_CachedEntry.m_wszFullKeyword)+1;
else
dwLength = wcslen(m_CachedEntry.m_wszKeyword)+1;
if( dwLength <= cchBuffer )
if( bFull )
wcscpy( pwszBuffer, m_CachedEntry.m_wszFullKeyword );
else
wcscpy( pwszBuffer, m_CachedEntry.m_wszKeyword );
else
*pwszBuffer = L'\0';
}
}
return bReturn;
}
BOOL CWordWheel::GetString( DWORD dwKeyword, CHAR* pszBuffer, DWORD cchBuffer, BOOL bFull, BOOL bCacheAll )
{
BOOL bReturn = FALSE;
if( pszBuffer && cchBuffer>0 ) {
WCHAR wszBuffer[HHWW_MAX_KEYWORD_LENGTH+1];
if( bReturn = GetString( dwKeyword, wszBuffer, cchBuffer, bFull, bCacheAll ) ) {
if( WideCharToMultiByte(CP_ACP, 0, wszBuffer, -1, pszBuffer, cchBuffer, NULL, NULL) == 0 )
bReturn = TRUE;
}
}
return bReturn;
}
DWORD CWordWheel::GetLevel( DWORD dwKeyword )
{
DWORD dwReturn = HHWW_ERROR;
if( GetIndexData( dwKeyword ) )
dwReturn = m_CachedEntry.m_dwLevel;
return dwReturn;
}
DWORD CWordWheel::GetLevelOffset( DWORD dwKeyword )
{
DWORD dwReturn = HHWW_ERROR;
if( GetIndexData( dwKeyword ) )
dwReturn = m_CachedEntry.m_dwLevelOffset;
return dwReturn;
}
BOOL CWordWheel::IsPlaceHolder( DWORD dwKeyword )
{
BOOL bReturn = FALSE;
if( GetIndexData( dwKeyword ) ) {
if( m_CachedEntry.m_dwFlags & HHWW_SEEALSO ) {
if( wcscmp( m_CachedEntry.m_wszSeeAlso, m_CachedEntry.m_wszFullKeyword ) == 0 ) {
bReturn = TRUE;
}
}
}
return bReturn;
}
BOOL CWordWheel::GetSeeAlso( DWORD dwKeyword, WCHAR* pwszBuffer, DWORD cchBuffer )
{
BOOL bReturn = FALSE;
if( pwszBuffer && cchBuffer>0 ) {
if( (bReturn = GetIndexData( dwKeyword ) ) ) {
if( m_CachedEntry.m_dwFlags & HHWW_SEEALSO ) {
if( (DWORD) (wcslen(m_CachedEntry.m_wszSeeAlso)+1) <= cchBuffer )
wcscpy( pwszBuffer, m_CachedEntry.m_wszSeeAlso );
else
*pwszBuffer = L'\0';
}
else
bReturn = FALSE;
}
}
return bReturn;
}
BOOL CWordWheel::GetSeeAlso( DWORD dwKeyword, CHAR* pszBuffer, DWORD cchBuffer )
{
BOOL bReturn = FALSE;
if( pszBuffer && cchBuffer>0 ) {
WCHAR wszBuffer[HHWW_MAX_KEYWORD_LENGTH+1];
if( bReturn = GetSeeAlso( dwKeyword, wszBuffer, cchBuffer ) ) {
if( WideCharToMultiByte(CP_ACP, 0, wszBuffer, -1, pszBuffer, cchBuffer, NULL, NULL) == 0 )
bReturn = TRUE;
}
}
return bReturn;
}
DWORD CWordWheel::GetHitCount( DWORD dwKeyword )
{
DWORD dwReturn = HHWW_ERROR;
if( GetIndexHitData( dwKeyword ) )
dwReturn = m_CachedResults.GetCount();
return dwReturn;
}
DWORD CWordWheel::GetHit( DWORD dwKeyword, DWORD dwHit, CExTitle** ppTitle )
{
DWORD dwReturn = HHWW_ERROR;
if( GetIndexHitData( dwKeyword ) ) {
dwReturn = m_CachedResults.GetAt( dwHit )->GetURLId();
if( ppTitle != NULL )
*ppTitle = m_CachedResults.GetAt( dwHit )->GetTitle();
}
return dwReturn;
}
inline BOOL CWordWheel::GetIndexHitData( const VOID* pcvKeywordObject, DWORD cbSize, HHKEYINFO* pInfo, DWORD dwKeyword )
{
if( !((pInfo->wFlags) & HHWW_UID_OVERFLOW) && pInfo->dwCount ) {
m_CachedResults.SetIndex( dwKeyword, pInfo->dwCount );
for( int i = 0; i < (int) pInfo->dwCount; i++ ) {
DWORD dwURLId = *((UNALIGNED DWORD*) (((DWORD_PTR)pcvKeywordObject) + cbSize + (i*sizeof(DWORD))) );
CExTitle* pTitle = m_pDatabase->GetTitle();
// if we are reading a word wheel that is to be merged then
// translate the URL Ids into 12/20 format
if( m_dwTitleId ) {
dwURLId = (m_dwTitleId<<20) + dwURLId;
}
// if we are reading a collection file then
// translate the URL Ids to standard format
// and set the title pointer appropriately
else if( m_pDatabase->IsCollection() ) {
DWORD dwTitleId = dwURLId>>20;
// v1.1a creates bogus entries in the chw file. So
// to workaround this we need to set the title id to 1
// and the url id to 0 when the title id is greater than
// the title count
if( dwTitleId > m_pDatabase->GetTitleMap()->GetCount() ) {
dwTitleId = 1;
dwURLId = 0;
}
else if( dwTitleId ) {
dwURLId = dwURLId & 0x000FFFFF;
pTitle = m_pDatabase->GetTitleMap()->GetAt(dwTitleId-1)->GetTitle();
}
}
m_CachedResults.GetAt( i )->SetURLId( dwURLId );
m_CachedResults.GetAt( i )->SetTitle( pTitle );
}
}
return TRUE;
}
BOOL CWordWheel::GetIndexData( DWORD dwKeyword, BOOL bCacheAll )
{
BOOL bReturn = FALSE;
if( Init() ) {
if( m_CachedEntry.m_dwIndex != dwKeyword ) {
BYTE KeywordObject[HHWW_MAX_KEYWORD_OBJECT_SIZE];
const VOID* pcvKeywordObject = KeywordObject;
if( SUCCEEDED(m_pWordWheel->Lookup( dwKeyword, KeywordObject, sizeof(KeywordObject) ) ) ) {
m_CachedEntry.m_dwIndex = dwKeyword;
wcscpy( m_CachedEntry.m_wszFullKeyword, (WCHAR*) pcvKeywordObject );
DWORD cbSize = sizeof(WCHAR) * (wcslen((WCHAR*)pcvKeywordObject) + 1);
HHKEYINFO* pInfo = (HHKEYINFO*)(((DWORD_PTR)(pcvKeywordObject))+cbSize);
m_CachedEntry.m_dwFlags = (DWORD) pInfo->wFlags;
m_CachedEntry.m_dwLevel = (DWORD) pInfo->wLevel;
m_CachedEntry.m_dwLevelOffset = pInfo->dwLevelOffset;
cbSize += sizeof(HHKEYINFO);
if( pInfo->wLevel )
wcscpy( m_CachedEntry.m_wszKeyword, (WCHAR*) (((DWORD_PTR)pcvKeywordObject)+(pInfo->dwLevelOffset*sizeof(WCHAR))) );
else
wcscpy( m_CachedEntry.m_wszKeyword, (WCHAR*) pcvKeywordObject );
if( (pInfo->wFlags) & HHWW_SEEALSO ) {
wcscpy( m_CachedEntry.m_wszSeeAlso, (WCHAR*)(((DWORD_PTR)pcvKeywordObject)+cbSize) );
}
if( bCacheAll ) {
GetIndexHitData( pcvKeywordObject, cbSize, pInfo, dwKeyword );
}
}
}
if( dwKeyword < GetCount() )
bReturn = TRUE;
}
return bReturn;
}
BOOL CWordWheel::GetIndexHitData( DWORD dwKeyword )
{
BOOL bReturn = FALSE;
if( Init() ) {
if( m_CachedResults.GetIndex() != dwKeyword ) {
BYTE KeywordObject[HHWW_MAX_KEYWORD_OBJECT_SIZE];
const VOID* pcvKeywordObject = KeywordObject;
if( SUCCEEDED(m_pWordWheel->Lookup( dwKeyword, KeywordObject, sizeof(KeywordObject) ) ) ) {
DWORD cbSize = sizeof(WCHAR) * (wcslen((WCHAR*)pcvKeywordObject) + 1);
HHKEYINFO* pInfo = (HHKEYINFO*)(((DWORD_PTR)(pcvKeywordObject))+cbSize);
cbSize += sizeof(HHKEYINFO);
GetIndexHitData( pcvKeywordObject, cbSize, pInfo, dwKeyword );
}
}
if( dwKeyword < GetCount() )
bReturn = TRUE;
}
return bReturn;
}
/////////////////////////////////////////////////////////////////////////////
// class CWordWheelCompiler implementation
void CWordWheelCompiler::_CWordWheelCompiler()
{
m_bInit = FALSE;
m_pszDatabase = NULL;
m_pwszKeywordLinks = NULL;
m_pwszAssociativeLinks = NULL;
m_lcid = 0;
m_pFileSystem = NULL;
m_pDatabase = NULL;
m_pPersistStorageDatabase = NULL;
m_pBuildCollectKeywordLinks = NULL;
m_pBuildCollectAssociativeLinks = NULL;
m_pPropList = NULL;
m_pStorageKeywordLinks = NULL;
m_pStorageAssociativeLinks = NULL;
m_pPersistStorageKeywordLinks = NULL;
m_pPersistStorageAssociativeLinks = NULL;
}
CWordWheelCompiler::CWordWheelCompiler( const CHAR* pszDatabase, const WCHAR* pwszKeywordLinks, const WCHAR* pwszAssociativeLinks, LCID lcid )
{
_CWordWheelCompiler();
m_pszDatabase = pszDatabase;
m_pwszKeywordLinks = pwszKeywordLinks;
m_pwszAssociativeLinks = pwszAssociativeLinks;
m_lcid = lcid;
}
CWordWheelCompiler::~CWordWheelCompiler()
{
Free();
}
HRESULT CWordWheelCompiler::Initialize()
{
HRESULT hr = S_FALSE;
// bail out if we are already initialized
if( m_bInit )
return S_OK;
// bail out if word wheel stream names not specified
if( !m_pwszKeywordLinks || !*m_pwszKeywordLinks ||
!m_pwszAssociativeLinks || !*m_pwszAssociativeLinks )
return S_FALSE;
// if the database is specified then use it otherwise generate
// a temporary filename
char szTempPath[MAX_PATH];
GetTempPath( sizeof(szTempPath), szTempPath );
if( m_pszDatabase && *m_pszDatabase ) {
strcpy( m_szDatabase, m_pszDatabase );
}
else {
GetTempFileName( szTempPath,"TFS",0, m_szDatabase );
}
// create the file system (delete the old one if it exists
if( IsFile( m_szDatabase ) )
DeleteFile( m_szDatabase );
m_pFileSystem = new CFileSystem;
if( !m_pFileSystem || FAILED( m_pFileSystem->Init() ) || FAILED( m_pFileSystem->CreateUncompressed( m_szDatabase ) ) ) {
return S_FALSE;
}
// get ITDatabase ptr.
if( SUCCEEDED(hr = CoCreateInstance(CLSID_IITDatabaseLocal, NULL, CLSCTX_INPROC_SERVER,
IID_IITDatabase, (void**)&m_pDatabase) ) ) {
if( SUCCEEDED(hr = m_pDatabase->QueryInterface( IID_IPersistStorage, (void**)&m_pPersistStorageDatabase ) ) ) {
if( SUCCEEDED(hr = m_pPersistStorageDatabase->InitNew( m_pFileSystem->GetITStorageDocObj() ) ) ) {
m_bInit = TRUE;
}
}
}
// create the sorter object
DWORD dwSorterInstance;
m_pDatabase->CreateObject( CLSID_HHSysSort, &dwSorterInstance );
// Create Build Collection objects
if( SUCCEEDED(hr = CoCreateInstance( CLSID_IITWordWheelUpdate, NULL, CLSCTX_INPROC_SERVER,
IID_IITBuildCollect, (VOID**)&m_pBuildCollectKeywordLinks ) ) ) {
if( SUCCEEDED(hr = CoCreateInstance( CLSID_IITWordWheelUpdate, NULL, CLSCTX_INPROC_SERVER,
IID_IITBuildCollect, (VOID**)&m_pBuildCollectAssociativeLinks ) ) ) {
// Create keyword Property List (used for both AssociativeLinks and KeywordLinks)
if( SUCCEEDED(hr = CoCreateInstance( CLSID_IITPropList, NULL, CLSCTX_INPROC_SERVER, IID_IITPropList, (VOID**)&m_pPropList) ) ) {
m_bInit = TRUE;
}
}
}
// create the substorage files
DWORD dwLen = 0;
CHAR* psz = NULL;
// KeywordLinks
WCHAR wszKeywordLinks[MAX_PATH];
m_pBuildCollectKeywordLinks->GetTypeString( wszKeywordLinks, NULL ); // Get the "$WW" prefix
wcscat( wszKeywordLinks, m_pwszKeywordLinks );
m_pFileSystem->GetITStorageDocObj()->CreateStorage( wszKeywordLinks, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &m_pStorageKeywordLinks);
m_pBuildCollectKeywordLinks->QueryInterface( IID_IPersistStorage, (void**)&m_pPersistStorageKeywordLinks );
m_pPersistStorageKeywordLinks->InitNew( m_pStorageKeywordLinks );
// AssociativeLinks
WCHAR wszAssociativeLinks[MAX_PATH];
m_pBuildCollectAssociativeLinks->GetTypeString( wszAssociativeLinks, NULL ); // Get the "$WW" prefix
wcscat( wszAssociativeLinks, m_pwszAssociativeLinks );
m_pFileSystem->GetITStorageDocObj()->CreateStorage( wszAssociativeLinks, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &m_pStorageAssociativeLinks);
m_pBuildCollectAssociativeLinks->QueryInterface( IID_IPersistStorage, (void**)&m_pPersistStorageAssociativeLinks );
m_pPersistStorageAssociativeLinks->InitNew( m_pStorageAssociativeLinks );
// get local information
char szCodePage[20] = "1252";
if( m_lcid == ((DWORD)-1) )
m_lcid = GetSystemDefaultLCID();
GetLocaleInfo(m_lcid,LOCALE_IDEFAULTANSICODEPAGE,szCodePage,sizeof(szCodePage) );
DWORD dwCodePage = Atoi( szCodePage );
// apply sorter object information to the new word wheels
VARARG vaDword = {0};
vaDword.dwArgc = 2;
vaDword.Argv[0] = (void *)(INT_PTR)IHHSK100_KEYTYPE_UNICODE_SZ;
vaDword.Argv[1] = (void*) 0;
VARARG vaEmpty = {0};
m_pBuildCollectKeywordLinks->InitHelperInstance( dwSorterInstance, m_pDatabase,
dwCodePage, m_lcid, vaDword, vaEmpty );
m_pBuildCollectAssociativeLinks->InitHelperInstance( dwSorterInstance, m_pDatabase,
dwCodePage, m_lcid, vaDword, vaEmpty );
if( FAILED(hr) ) {
Free();
m_bInit = FALSE;
}
else
m_bInit = TRUE;
return hr;
}
HRESULT CWordWheelCompiler::Free()
{
HRESULT hr = S_FALSE;
if( m_pPersistStorageKeywordLinks ) {
m_pPersistStorageKeywordLinks->Release();
m_pPersistStorageKeywordLinks = NULL;
}
if( m_pPersistStorageAssociativeLinks ) {
m_pPersistStorageAssociativeLinks->Release();
m_pPersistStorageAssociativeLinks = NULL;
}
if( m_pStorageKeywordLinks ) {
m_pStorageKeywordLinks->Release();
m_pStorageKeywordLinks = NULL;
}
if( m_pStorageAssociativeLinks ) {
m_pStorageAssociativeLinks->Release();
m_pStorageAssociativeLinks = NULL;
}
if( m_pPropList ) {
m_pPropList->Release();
m_pPropList = NULL;
}
if( m_pBuildCollectAssociativeLinks ) {
m_pBuildCollectAssociativeLinks->Release();
m_pBuildCollectAssociativeLinks = NULL;
}
if( m_pBuildCollectKeywordLinks ) {
m_pBuildCollectKeywordLinks->Release();
m_pBuildCollectKeywordLinks = NULL;
}
if( m_pPersistStorageDatabase ) {
m_pPersistStorageDatabase->Release();
m_pPersistStorageDatabase = NULL;
}
if( m_pDatabase ) {
m_pDatabase->Close();
m_pDatabase->Release();
m_pDatabase = NULL;
}
if( m_pFileSystem ) {
delete m_pFileSystem;
m_pFileSystem = NULL;
}
m_bInit = FALSE;
hr = S_OK;
return hr;
}
HRESULT CWordWheelCompiler::Build()
{
HRESULT hr = S_FALSE;
if( Init() ) {
#ifdef CHIINDEX
if ( m_bAnimation )
#endif
NextAnimation();
// KeywordLinks
m_pPersistStorageKeywordLinks->Save( m_pStorageKeywordLinks, TRUE );
#ifdef CHIINDEX
if ( m_bAnimation )
#endif
NextAnimation();
m_pPersistStorageKeywordLinks->Release();
m_pPersistStorageKeywordLinks = NULL;
#ifdef CHIINDEX
if ( m_bAnimation )
#endif
NextAnimation();
m_pStorageKeywordLinks->Commit(STGC_DEFAULT);
#ifdef CHIINDEX
if ( m_bAnimation )
#endif
NextAnimation();
// AssociativeLinks
m_pPersistStorageAssociativeLinks->Save( m_pStorageAssociativeLinks, TRUE );
#ifdef CHIINDEX
if ( m_bAnimation )
#endif
NextAnimation();
m_pPersistStorageAssociativeLinks->Release();
m_pPersistStorageAssociativeLinks = NULL;
#ifdef CHIINDEX
if ( m_bAnimation )
#endif
NextAnimation();
m_pStorageAssociativeLinks->Commit(STGC_DEFAULT);
#ifdef CHIINDEX
if ( m_bAnimation )
#endif
NextAnimation();
// Database == $OBJINST file
m_pPersistStorageDatabase->Save( m_pFileSystem->GetITStorageDocObj(), TRUE );
#ifdef CHIINDEX
if ( m_bAnimation )
#endif
NextAnimation();
m_pPersistStorageDatabase->Release();
m_pPersistStorageDatabase = NULL;
#ifdef CHIINDEX
if ( m_bAnimation )
#endif
NextAnimation();
m_pFileSystem->GetITStorageDocObj()->Commit(STGC_DEFAULT);
#ifdef CHIINDEX
if ( m_bAnimation )
#endif
NextAnimation();
}
return hr;
}