|
|
// 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; }
|